Replacement to system("color") - c++

I need a method that acts like system color without using a system call. I know there is setConsoleTextAttribute() but that doesn't fill the full foreground and background with color only new characters. I am using windows7 although i would like to make this compatible with all windows

As per your comments, here is a more complete solution to the problem you are trying to solve. It is based on my original answer (which can be found at the end of this answer).
I discovered one limitation in Windows API, namely that it cannot read the entire screen buffer for a default console mode window of 80 columns by 300 rows. It results in a ERROR_NOT_ENOUGH_MEMORY error due to insufficient Windows process heap (as best as I can tell from some Google searching). I wound up implementing a wrapper around the XxxConsoleOutput functions to support automatic subdivision of the screen buffer region as needed until the function succeeds or it fails to read a 1 X 1 (single character) region.
Again, this code is probably not perfect, it is meant to explain concepts, not to (necessarily) provide you with a complete solution. However, it now fills the entire console (previously I only filled the visible portion of the window) and sets the console's text attribute for future output.
Skip this code to read the original answer.
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <iostream>
#include <vector>
using namespace std;
// Split a rectangular region into two smaller rectangles
// based on the largest dimension.
void SplitRegion(
SHORT width, SHORT height,
COORD dwBufferCoord, const SMALL_RECT& readRegion,
COORD& dwBufferCoordA, SMALL_RECT& readRegionA,
COORD& dwBufferCoordB, SMALL_RECT& readRegionB)
{
dwBufferCoordA = dwBufferCoordB = dwBufferCoord;
readRegionA = readRegionB = readRegion;
if (height >= width)
{
SHORT half = height / 2;
dwBufferCoordB.Y += half;
readRegionB.Top += half;
readRegionA.Bottom = readRegionB.Top - 1;
}
else
{
SHORT half = width / 2;
dwBufferCoordB.X += half;
readRegionB.Left += half;
readRegionA.Right = readRegionB.Left - 1;
}
}
// Utility function to figure out the distance
// between two points.
template <typename type>
inline type DiffHelper(type first, type second)
{
return (second >= first) ? (second - first + 1) : 0;
}
// A template that wraps up the shared code common between
// reading and writing the screen buffer. If it is ever
// given a region of zero width or height, it will
// "succeed". If it ever tries to subdivide a 1 by 1
// region, it will fail.
template <typename lpBufferType>
BOOL XferConsoleOutputWrapper(
HANDLE hConsoleOutput,
lpBufferType lpBuffer,
COORD dwBufferSize,
COORD dwBufferCoord,
SMALL_RECT& xferRegion,
BOOL (WINAPI * xferConsoleOutput)(
HANDLE, lpBufferType, COORD, COORD, PSMALL_RECT))
{
SHORT width = DiffHelper(xferRegion.Left, xferRegion.Right);
SHORT height = DiffHelper(xferRegion.Top, xferRegion.Bottom);
if ((width == 0) || (height == 0))
{
return TRUE;
}
BOOL success = xferConsoleOutput(hConsoleOutput,
lpBuffer, dwBufferSize, dwBufferCoord, &xferRegion);
if (!success)
{
if ((GetLastError() == ERROR_NOT_ENOUGH_MEMORY) &&
((width * height) > 1))
{
COORD dwBufferCoordA, dwBufferCoordB;
SMALL_RECT xferRegionA, xferRegionB;
SplitRegion(
width, height,
dwBufferCoord, xferRegion,
dwBufferCoordA, xferRegionA,
dwBufferCoordB, xferRegionB);
success =
XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoordA, xferRegionA, xferConsoleOutput) &&
XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoordB, xferRegionB, xferConsoleOutput);
}
}
return success;
}
// ReadConsoleOutput failed to read an 80 by 300 character screen
// buffer in a single call, resulting in ERROR_NOT_ENOUGH_MEMORY.
// ReadConsoleOutputWrapper will subdivide the operation into
// smaller and smaller chunks as needed until it succeeds in reading
// the entire screen buffer.
inline BOOL ReadConsoleOutputWrapper(
HANDLE hConsoleOutput,
PCHAR_INFO lpBuffer,
COORD dwBufferSize,
COORD dwBufferCoord,
SMALL_RECT& readRegion)
{
return XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoord, readRegion, ReadConsoleOutput);
}
// WriteConsoleOutputWrapper will subdivide the operation into
// smaller and smaller chunks as needed until it succeeds in writing
// the entire screen buffer. This may not be necessary as
// WriteConsoleOutput never failed, but it was simple to implement
// so it was done just to be safe.
inline BOOL WriteConsoleOutputWrapper(
HANDLE hConsoleOutput,
const CHAR_INFO* lpBuffer,
COORD dwBufferSize,
COORD dwBufferCoord,
SMALL_RECT& writeRegion)
{
return XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize,
dwBufferCoord, writeRegion, WriteConsoleOutput);
}
void ConsoleFillWithAttribute(WORD fillAttribute)
{
// Get the handle to the output
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
// Get the information for the current screen buffer
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(hStdout, &info);
// Allocate a vector to hold the visible screen buffer data
vector<CHAR_INFO> buffer(info.dwSize.X * info.dwSize.Y);
// Initialize a couple of pointers to the begin and end of the buffer
CHAR_INFO* begin = buffer.data();
CHAR_INFO* end = begin + buffer.size();
// Start at the upper left corner of the screen buffer.
COORD coord;
coord.X = coord.Y = 0;
// Initialize the region to encompass the entire screen buffer.
SMALL_RECT region;
region.Left = region.Top = 0;
region.Right = info.dwSize.X - 1;
region.Bottom = info.dwSize.Y - 1;
// Read the buffer from the console into the CHAR_INFO vector.
ReadConsoleOutputWrapper(hStdout, buffer.data(), info.dwSize, coord, region);
// Change all the attributes to the specified fill attribute.
while (begin != end)
{
begin->Attributes = fillAttribute;
++begin;
}
// Write the buffer from the CHAR_INFO vector back to the console.
WriteConsoleOutputWrapper(hStdout, buffer.data(), info.dwSize, coord, region);
// Finally, set the console text attribute to the fill attribute
// so that all new text will be printed in the same manner as
// the attributes we just changed.
SetConsoleTextAttribute(hStdout, fillAttribute);
}
int main()
{
cout << "I would like to fill up the console with some text." << endl;
cout << "The quick brown fox jumped over the lazy dogs." << endl;
for (int i = 0; i < 100; ++i)
{
cout << ' ' << i;
}
cout << endl;
ConsoleFillWithAttribute(
BACKGROUND_BLUE | FOREGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN);
cout << endl;
cout << "This should also be printed in the new attribute" << endl;
return 0;
}
The original answer is below:
If I understand what you are asking, you want to be able to change the attributes for the entire console mode window. You might want to look into the ReadConsoleOutput and WriteConsoleOutput functions. You can use ReadConsoleOutput to read some or all of a console buffer into memory, then manipulate the attribute data as needed for your application, then use WriteConsoleOutput to write memory back to the console output buffer.
Here is some code that will change the attributes for the currently displayed portion of console (assuming output has not been redirected to a non-console handle):
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <iostream>
#include <vector>
using namespace std;
void ConsoleFillDisplayWithAttribute(WORD fillAttribute)
{
// Get the handle to the output
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
// Get the information for the current screen buffer
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(hStdout, &info);
// Calculate the size of the displayed portion of the screen buffer
COORD size;
size.X = (info.srWindow.Right - info.srWindow.Left + 1);
size.Y = (info.srWindow.Bottom - info.srWindow.Top + 1);
// Allocate a vector to hold the visible screen buffer data
vector<CHAR_INFO> buffer(size.X * size.Y);
COORD coord;
coord.X = coord.Y = 0;
// Read the buffer from the console into the CHAR_INFO vector
ReadConsoleOutput(hStdout, buffer.data(), size, coord, &info.srWindow);
// Initialize a couple of pointers to the begin and end of the buffer
CHAR_INFO* begin = buffer.data();
CHAR_INFO* end = begin + buffer.size();
// Change all the attributes to the specified fill attribute
while (begin != end)
{
begin->Attributes = fillAttribute;
++begin;
}
// Write the buffer from the CHAR_INFO vector back to the console
WriteConsoleOutput(hStdout, buffer.data(), size, coord, &info.srWindow);
}
int main()
{
cout << "I would like to fill up the console with some text." << endl;
cout << "The quick brown fox jumped over the lazy dogs." << endl;
for (int i = 0; i < 100; ++i)
{
cout << ' ' << i;
}
cout << endl;
ConsoleFillDisplayWithAttribute(
BACKGROUND_BLUE | FOREGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN);
return 0;
}

Related

MOUSEEVENTF_MOVE doesn't move to correct coordinates in-game

Feel like I am bashing my head against the wall here so wanted to reach out and see if there is some easy solution I am missing first before I go crazy.
The problem:
I was tasked to write a TAS for an older directx11 game for a charity event. I want to detect a pixel color, and move to that pixel. I have pixel detection working via OpenCV but the actual movements to that pixel do not line up.
I found a function that does what I want, but it uses a fixed number to try and modify the movement by that I can't figure out for my game.
My mouse dpi does not affect the movement, but the in-game sensitivity does.
The game does not like SetCursorPos and seems to reject that input.
Code:
#include <iostream>
#include <Windows.h>
using namespace std;
//Magic number defines
const float constant = 0.116f;
float mouseSensitivity = 10.0f;
float modifier = mouseSensitivity * constant;
int centerScreenX = GetSystemMetrics(SM_CXSCREEN) / 2;
int centerScreenY = GetSystemMetrics(SM_CYSCREEN) / 2;
//Move mouse to center of screen
void centerMouse() {
SetCursorPos(centerScreenX, centerScreenY);
cout << "Moving to center of screen at(" << centerScreenX << ", " << centerScreenY << ")" << endl;
}
//Calculates actual coordinates for mouse movement based on sensitivity and a constant.
void calibrateCoordinates(int& x, int& y)
{
if (abs(x) < 5)
x = 0;
else {
x = x - centerScreenX;
x = (int)((float)x / modifier);
}
if (abs(y) < 5)
y = 0;
else
{
y = y - centerScreenY;
y = (int)((float)y / modifier);
}
cout << "Coordinates needed to move by (" << x << ", " << y << ")" << endl;
}
// Moves to x,y coordinates after processed into actual coordinates based on sensitivity and a constant.
void moveTo(int x, int y)
{
SetProcessDPIAware();
calibrateCoordinates(x, y);
mouse_event(MOUSEEVENTF_MOVE, x, y, 0, 0);
//Sanity check where the mouse ended up at
POINT p;
if (GetCursorPos(&p))
{
cout << "Mouse ended up at (" << p.x << ", " << p.y << ")" << endl;
}
}
int main() {
while (true) {
// Check if the F19 button is pressed
if (GetAsyncKeyState(VK_F19) & 0x8000) {
//Hardcoded values of pixel we need to move to. Handled automatically via OpenCV in the real code. Simplified here
int xTo = 784;
int yTo = 686;
//Centers mouse to line up cursor with crosshair
centerMouse();
//Tries to move to coords
moveTo(xTo, yTo);
}
}
return 0;
}
Output:
Matched pixel found at (784, 686)[4, 20, 222, 255]
Moving to center of screen at (1280, 720)
Coordinates needed to move by (-271, -20)
Mouse ended up at (1009, 700)
The mouse should have ended up at (1012, 649) for the cross-hair to line up with the pixel I want. Do I just need to keep experimenting to find the magic number that it works with? Or is there any easier way to do this? Thanks
GetSystemMetrics function is not DPI aware. Use GetSystemMetricsForDpi instead to get a correct result.
Disclaimer: Sorry if this isn't an answer but I can't comment yet because of my low rep.
If it's a game, have you tried taking the DX9/11's version of "GetSystemMetrics"? I had the same problem on DX9 aimbot years ago where the mouse aims slightly to the right. Now I can't give the exact answer since I've never done DX11 hooking but this is the mouse_move code for my aimbot:
mouse_event(MOUSEEVENTF_MOVE, coordinates.x - interfaceinfo->D3D9Viewport.Width * 0.5f, coordinates.y - interfaceinfo->D3D9Viewport.Height * 0.5f, 0, 0);
The coordinates is the target's position, while interfaceinfo->D3D9Viewport.Width/Height is the screen info given by the DX9, in this case using **IDirect3DDevice9::GetViewport**
HRESULT WINAPI Renderer::hkPresent(LPDIRECT3DDEVICE9 pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) {
renderer->Device_D3D9 = pDevice;
Device_D3D9->GetViewport(&interfaceinfo->D3D9Viewport);
return renderer->oPresent(pDevice, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
}
If it's a game, have you tried moving the mouse by hooking DINPUT functions.
By hooking to GetDeviceState and GetDeviceData, you can emulate keyclicks and mouse clicks/movement without moving your actual mouse (unlike sendinput and mouse_event)
HRESULT WINAPI Renderer::hkGetDeviceState(IDirectInputDevice8A* DIDevice, DWORD cbData, LPVOID *lpvData) {
HRESULT hResult = renderer->oGetDeviceState(DIDevice, cbData, lpvData);
if (interfaceinfo->bCurrentWindow >= 1) {
if (hResult == DI_OK) {
for (auto i = 0; i < cbData; i++) {
lpvData[i] = 0;
}
}
}
static BYTE buffer[256];
if (true) {
buffer[DIK_W] = LOBYTE(0x80);
buffer['w'] = LOBYTE(0x80);
buffer['W'] = LOBYTE(0x80);
cbData = 256;
memcpy(lpvData, buffer, cbData);
}
else {
hResult = renderer->oGetDeviceState(DIDevice, cbData, lpvData);
}
return hResult;
}
HRESULT WINAPI Renderer::hkGetDeviceData(IDirectInputDevice8A* DIDevice, DWORD cbObjectData, LPDIDEVICEOBJECTDATA rgdod, LPDWORD pdwInOut, DWORD dwFlags) {
HRESULT ret = renderer->oGetDeviceData(DIDevice, cbObjectData, rgdod, pdwInOut, dwFlags);
if (ret == DI_OK) {
for (DWORD i = 0; i < *pdwInOut; ++i) {
if (interfaceinfo->bCurrentWindow >= 1) {
*pdwInOut = 0;
}
else {
switch (rgdod[i].dwOfs) {
case DIK_W:
rgdod[i].dwData = LOBYTE(0x80);
break;
case DIK_SPACE: {
static byte last_written = 0;
rgdod[i].dwData = HIBYTE(last_written ? 0x01 : 0x00);
last_written = last_written ? 0x00 : 0x01;
break;
}
}
}
}
}
return ret;
}
First of all, you appear to be passing absolute coordinates to the function mouse_event, but that function is interpreting them as coordinates relative to the last mouse position, because you are not passing the MOUSEEVENTF_ABSOLUTE flag to mouse_event. Therefore, if you want to pass absolute coordinates, you should also pass that flag. Also, Microsoft recommends that you use SendInput instead of mouse_event.
Another issue is the following:
The official Microsoft documentation for the function GetSystemMetrics states the following:
This API is not DPI aware, and should not be used if the calling thread is per-monitor DPI aware. For the DPI-aware version of this API, see GetSystemMetricsForDPI. For more information on DPI awareness, see the Windows High DPI documentation.
Therefore, the results of the lines
int centerScreenX = GetSystemMetrics(SM_CXSCREEN) / 2;
int centerScreenY = GetSystemMetrics(SM_CYSCREEN) / 2;
are possibly incorrect. I suggest that you change these lines to the following:
int centerScreenX = GetSystemMetricsForDpi( SM_CXSCREEN, GetDpiForSystem() ) / 2;
int centerScreenY = GetSystemMetricsForDpi( SM_CYSCREEN, GetDpiForSystem() ) / 2;
However, the function GetDpiForSystem will simply return the value 96 unless the process or thread is made DPI-aware beforehand. Therefore, it is important that you call the function SetProcessDPIAware before calling the function GetDPIForSystem. Alternatively, you can set the default DPI awareness through an application manifest setting, as described here.
According to this Microsoft documentation, there are currently 4 different modes of DPI awareness:
Unaware
System
Per-Monitor
Per-Monitor V2
Since you are using the function SetProcessDPIAware, you are setting the DPI awareness mode of your process to "System". This mode will only make your process aware of the DPI on the primary monitor at the time the user logged in. If the DPI was changed since then, then your process will be unaware of these changes. For this reason, you may want to consider using "Per-Monitor V2" DPI-awareness instead, by using the function SetProcessDpiAwarenessContext or SetThreadDpiAwarenessContext. However, this will require your application to handle WM_DPICHANGED messages.

My code is printing strange characters instead of what it is meant to print

I'm trying to make a simple console game, and I am making a sort of console graphics engine that draws a screen with a map and some text. For some reason the engine writes strange characters to the console, rather than what is meant to be written.
This engine takes two 2d vectors that describe the characters and colors to be used in the console. I am using WriteConsole() to write to the console, and using SetConsoleTextAttribute() to change the color of the text as I draw. For some reason, when I try to print text, it writes some really weird characters that have no relation to the characters that are meant to be printed. Colors work just fine though. My characters are stored as TCHARs and my colors as ints.
My function to actually draw the screen:
void update()
{
for (int y = 0; y < SCREEN_HEIGHT; y++) //loop through all of the tiles
{
for (int x = 0; x < SCREEN_WIDTH; x++)
{
if (screen.at(y).at(x) != buffer.at(y).at(x) && screenColors.at(y).at(x) != bufferColors.at(y).at(x)) //only draw the tile if it has changed
{
pos.X = x; //set coords of cursor to tile to be drawn
pos.Y = y;
SetConsoleTextAttribute(hStdOut, bufferColors.at(y).at(x)); //set the text color
SetConsoleCursorPosition(hStdOut, pos); //move the cursor to the tile to be drawn
WriteConsole(hStdOut, &(buffer.at(y).at(x)), 1, dw, NULL); //actually draw the tile
screen.at(y).at(x) = buffer.at(y).at(x); //update 2d screen vector (used to read what is on the screen for other reasons)
}
}
}
SetConsoleCursorPosition(hStdOut, restPos); //move the cursor away, so it doesn't look ugly
}
My function to write to the buffer vector:
void draw2dVector(int x, int y, vector<vector<TCHAR>> draw, vector<vector<int>> colors)
{
for (unsigned int drawY = 0; drawY < draw.size(); drawY++)
{
for (unsigned int drawX = 0; drawX < draw.front().size(); drawX++)
{
buffer.at(y + drawY).at(x + drawX) = colors.at(drawY).at(drawX); // <- I found the problem. I am writing color ints to the buffer.
bufferColors.at(y + drawY).at(x + drawX) = colors.at(drawY).at(drawX);
}
}
}
My function to convert strings to vector<TCHAR>s.
vector<TCHAR> stringToTCHARvector(string str, int strLen) //convert a TCHAR string to a TCHAR vector
{
vector<TCHAR> result;
for (int i = 0; i < strLen; i++) //loop through the characters in the string
{
result.push_back(str[i]); //add the character to the TCHAR vector
}
return result;
}
I expect the output to look something like this:
###....### Inventory:
####....## Gold Piece
#..##...##
#......###
###.#..###
##########
But instead it gives me this:
êêêêêêêêêê *insert strange characters here, because stack overflow doesn't
êêêêêwwêêê show them*
êêêêwwwwww
êêêêwwwwww
êêêêêwwwww
êêêêêwwwww
UPDATE:
It turns out that the problem was that I was writing the ints from my color vector to my TCHAR buffer. I have resolved the issue. Thank you for your help.

How to clear an area from text with WriteConsoleOutputCharacter()?

So, I have a C++ class that represents rectangles, I use the WriteConsoleOutputCharacter function that outputs faster than cout or printf(), I've already made a program that prints the rectangle but I'm having trouble clearing the rectangle out.
From my understanding from msdn this function can print unicode characters or 8-bit characters from the console's current code page. Anyway, when I wanted to print backspace in order to clear the rectangle it didn't work and it prints something else(◘). When I tried to print backspace through it's hex code (0x008) it printed the symbol again.
The code is quite simple:
const char clr[] ="\b";//Thar the array I'm printing
void rect_m::clr_ar()
{
Ex = Vx + Lx;//These variables are the rectangle's sizes
Ey = Vy + Ly;
HANDLE mout = GetStdHandle(STD_OUTPUT_HANDLE);
//The loops cover the rectangle area
for (SHORT i = Vy; i < Ey; i++)
{
for (SHORT j = Vx; j < Ex; j++)
{
WriteConsoleOutputCharacter(mout, clr, strlen(clr), { j,i }, &dwWritten);
}
}
}
Well, all I want is a way to print backspace with the WriteConsoleOutputCharacter function to clear the text out (and not by printing spaces over it). I know that's a very basic mistake and that there is a better way. So,can someone tell me please what's wrong with my code?
for clear rectangle area we can use ScrollConsoleScreenBufferW for fill selected rectangle with blank characters. note that blank characters is equal to empty space, which we can view in test, if call ReadConsoleOutputCharacter at begin on yet empty console:
COORD xy{};
ULONG n;
WCHAR c;
ReadConsoleOutputCharacterW(hConsoleOutput, &c, 1, xy, &n);
//c == ' ';
so full code can look like:
BOOL cls(const SMALL_RECT* lpScrollRectangle = 0)
{
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hConsoleOutput, &csbi))
{
CHAR_INFO fi = { ' ', csbi.wAttributes };
if (!lpScrollRectangle)
{
csbi.srWindow.Left = 0;
csbi.srWindow.Top = 0;
csbi.srWindow.Right = csbi.dwSize.X - 1;
csbi.srWindow.Bottom = csbi.dwSize.Y - 1;
lpScrollRectangle = &csbi.srWindow;
}
return ScrollConsoleScreenBufferW(hConsoleOutput, lpScrollRectangle, 0, csbi.dwSize, &fi);
}
return FALSE;
}

X and Y coordinate from Precision touchpad with raw input

Background
I am trying to get the touch coordinates from precision touchpads with C++ in Win10 with the program running in background.
Research
Rawinput can get data in background.
I can get the data with these usage id and pages from microsoft :
Member Description Page ID Mandatory/Optional
X X coordinate of contact position 0x01 0x30 Mandatory for T Optional for C
Y Y coordinate of contact position 0x01 0x31 Mandatory for T Optional for C
I can put the two of them together with HIDP_functions, from this answer.
Code
case WM_INPUT: {
UINT dwSize;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER));
LPBYTE lpb = new BYTE[dwSize];
if (lpb == NULL) {
return 0;
}
RAWINPUT* raw = (RAWINPUT*)lpb;
GetRawInputDeviceInfo(raw->header.hDevice, RIDI_PREPARSEDDATA, NULL, &dwSize);
PHIDP_PREPARSED_DATA preparsedData = (PHIDP_PREPARSED_DATA)HeapAlloc(GetProcessHeap(), 0, dwSize);
GetRawInputDeviceInfo(raw->header.hDevice, RIDI_PREPARSEDDATA, preparsedData, &dwSize);
HIDP_CAPS caps;
HidP_GetCaps(preparsedData, &caps);
USHORT capsLength = caps.NumberInputValueCaps;
PHIDP_VALUE_CAPS valueCaps = (PHIDP_VALUE_CAPS)HeapAlloc(GetProcessHeap(), 0, capsLength*sizeof(HIDP_VALUE_CAPS));
HidP_GetValueCaps(HidP_Input, valueCaps, &capsLength, preparsedData);
for (int i=0; i < capsLength; i++) {
CHAR value;
USHORT valuelength = valueCaps[i].BitSize * valueCaps[i].ReportCount;
HidP_GetUsageValueArray (HidP_Input, valueCaps[i].UsagePage, 0, valueCaps[i].NotRange.Usage, &value, valuelength, preparsedData, (PCHAR)raw->data.hid.bRawData, raw->data.hid.dwSizeHid);
std::cout << valueCaps[i].UsagePage << " " << valueCaps[i].NotRange.Usage <<std::endl;
std::cout << value << std::endl;
switch(valueCaps[i].NotRange.Usage) {
case 0x30: // X-axis
std::cout << "X: " << value << std::endl;
break;
case 0x31: // Y-axis
std::cout << "y: " << value << std::endl;
break;
}
}
}
Problem
I compiled the code and touch my touchpad, but all the outputs are:
0 0
³
Have I done anything wrong? Does anyone have any idea?
A packet of minimal Contact ID, x, y, Contact, Scan may contain multiple Contact ID, x, y triplets for multiple points and then must be used
valueCaps[i].LinkCollection from valueCaps[] array which increases from 1 for up to five points.
HidP_GetUsageValue
(
HidP_Input,
valueCaps[i].UsagePage,
valueCaps[i].LinkCollection, //** !!!!!
valueCaps[i].NotRange.Usage,
&value,
preparsedData,
(PCHAR)raw->data.hid.bRawData,
raw->data.hid.dwSizeHid
);
Coordinates are in array if valueCaps[i].UsagePage = 1 and
valueCaps[i].NotRange.Usage = 0x30 (x), valueCaps[i].NotRange.Usage = 0x30 (y)
Very important are xLogicalMin, yLogicalMin, xLogicalMax, yLogicalMax in valueCaps[i] array
xLogicalMin = valueCaps[i].LogicalMin;
xLogicalMax = valueCaps[i].LogicalMax;
xPhysicalMin = valueCaps[i].PhysicalMin;
xPhysicalMax = valueCaps[i].PhysicalMax;
All allocated spaces must be correct released !!!!!
Pressing оп perimeter points of touchpad for initiate multiple functions, or cells of grid on touchpad as on ASUS NumberPad also with limited functions of capacitive and active pen, one finger flicks, moving along four sides of touchpad for scrolling / scale, moving cursor with one finger and click, press, press plus move,
double click, click plus press second finger. Two finger operations count increased at finger space near or far possible with coordinates. With coordinates, times in "Scan" and contacts in "Contact" all possible complex gestures on touchpad are accessible.
Call of Your Program
HidP_GetUsageValueArray (HidP_Input, valueCaps[i].UsagePage, 0, valueCaps[i].NotRange.Usage, &value, valuelength, preparsedData, (PCHAR)raw->data.hid.bRawData, raw->data.hid.dwSizeHid);
returns HIDP_STATUS_NOT_VALUE_ARRAY = 0xc011000b
In Array valueCaps[] on my notebook with PTP are valueCaps[i].UsagePage = 1 and valueCaps[i].NotRange.Usage = 0x30 / 0x31. You can use simple
HidP_GetUsageValue
(
HidP_Input,
valueCaps[i].UsagePage,
0,
valueCaps[i].NotRange.Usage,
&value,
preparsedData,
(PCHAR)raw->data.hid.bRawData,
raw->data.hid.dwSizeHid
);
with 1 / 30h, 1 / 31h
corresponding to
_Must_inspect_result_
NTSTATUS __stdcall
HidP_GetUsageValue(
In HIDP_REPORT_TYPE ReportType,
In USAGE UsagePage,
_In_opt_ USHORT LinkCollection,
In USAGE Usage,
Out PULONG UsageValue,
In PHIDP_PREPARSED_DATA PreparsedData,
_In_reads_bytes_(ReportLength) PCHAR Report,
In ULONG ReportLength
);
and value ( UsageValue ) must be LONG !!!!!.
First value at WM_INPUT comes with UsagePage 0x0D Usage 0x51
Contact ID Uniquely identifies the contact within a given frame 0x0D 0x51 Mandatory

Get HBITMAPs For *ALL* Sizes and Depths of a File Type Icon (C++)

Compiler: MinGW/GCC
I'm trying to get the HICON of a file type based on what icon windows has registered for that file type, and then grab all of the HICON's images.
The problem is, I can't seem to get anything other than the 32x32 or 16x16 icon. Also, I've looked at GetIconInfoEx() but that function doesn't allow me to choose the icon size that I'm wanting, it just sort of arbitrarily pukes up whatever Windows feels like handing me at the time.
I want to at least have all of the 16x16, 32x32, and 48x48 icons, but I would really enjoy being able to extract every size that's in the HICON that I pass in.
Here's the code I'm currently working with (copy and pasted most of this from the web and stitched it together):
HBITMAP GetFileTypeIcon(const char* ext, int type, int depth)
{
HICON hIcon;
SHFILEINFO sfi= {0};
UINT flag = SHGFI_ICON|SHGFI_USEFILEATTRIBUTES;
int wh = 16;
switch(type)
{
default:
case FILE_ICON_SIZE_16:
{
wh = 16; flag|=SHGFI_SMALLICON;
}
break;
case FILE_ICON_SIZE_32:
{
wh = 32; flag|=SHGFI_LARGEICON;
}
break;
case FILE_ICON_SIZE_48:
{
wh = 48; flag|=SHGFI_SYSICONINDEX;
}
break;
case FILE_ICON_SIZE_256:
{
wh = 256; flag|=SHGFI_SYSICONINDEX;
}
break;
}
HRESULT hr = SHGetFileInfo(ext,FILE_ATTRIBUTE_NORMAL,&sfi,sizeof(sfi),flag);
if(SUCCEEDED(hr))
{
if((type == FILE_ICON_SIZE_48) || (type == FILE_ICON_SIZE_256))
{
// THIS PART DOESN'T COMPILE: undeclared function/indentifiers
// HIMAGELIST* imageList;
// hr = SHGetImageList(((type == FILE_ICON_SIZE_256)?SHIL_JUMBO:SHIL_EXTRALARGE), IID_IImageList, (void**)&imageList);
// if(SUCCEEDED(hr))
// {
// //Get the icon we need from the list. Note that the HIMAGELIST we retrieved
// //earlier needs to be casted to the IImageList interface before use.
// hr = ((IImageList*)imageList)->GetIcon(sfi.iIcon, ILD_TRANSPARENT, &hIcon);
// }
}
else
{
hIcon=sfi.hIcon;
}
}
// Convert to an HBITMAP (to get it out of the icon...)
HDC hDC = GetDC(NULL);
HDC hMemDC = CreateCompatibleDC(hDC);
HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, wh, wh);
HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp);
DrawIconEx(hMemDC, 0, 0, hIcon, wh, wh, 0, NULL, DI_NORMAL);
SelectObject(hMemDC, hOrgBMP);
DeleteDC(hMemDC);
ReleaseDC(NULL, hDC);
DestroyIcon(hIcon);
return hMemBmp;
}
I don't even know what to do about color depths. I'll hazard a guess: make a DC that has a certain color depth (rather than just a compatible DC) and pass that into DrawIconEx()?
Edit: I answered my own question after much research/work.
See my answer below for a way to find and parse the raw icon data.
I basically had to do everything myself (with the help of the web, Stack Overflow, and several MSDN articles) so I think I'll just post my own solution here.
I ended up parsing the registry to find the locations of the icons of each previously registered file extension, since the API functions that should have easily gotten me the information I wanted have some... problems.
After that I spent several days manually observing the data formats at hand by observing output of an icon program, and with this information in hand I was able to construct an image loader.
I used Allegro game library to make dealing with BITMAP images easier - Win32/GDI is a bit too much to deal with and would have made the code exorbitantly messy.
Finding the Icon Location and Index:
(1) Look for extension under HKEY_CLASSES_ROOT, eg HKCR\.foo\(default) = "foofile"
(2) Default data of this is the next key to look at, eg HKCR\foofile\
(3) Default data here is the description eg HKCR\foofile\(default) = "Foo Document"
(4) The icon location may be in one of two places that I know of:
Either in HKCR\foofile\DefaultIcon\(default) or there may be an entry something like HKCR\foofile\CurVer\(default) = "foofile.1" which tells you to look at the key HKCR\foofile.1\DefaultIcon\(default) for the icon location.
Parsing the Icon Location String:
The string is simply a path followed by a comma, white space, possibly a negative sign, and a number indicating the "index" of the icon.
Here's the big gotcha: Let the icon index be N. If N is negative (might want to check for negative zeros!), it is a resource ID within the file specified. If N is positive, it means to find the N'th icon within the file, but the icon is not necessarily at resource ID number N.
Parsing Icon Structures Manually:
This is the bulk of the code and time spent, but it works beautifully. First off, here's the data formats for the various sections of color and mask data.
Data Block Formats:
32bit ... Color Data:
====================================================================================
Little Endian 4 byte ARGB values.
The rows are stored in reverse order (bottom to top).
24bit ... Color Data:
====================================================================================
Little Endian 3 byte RGB values.
Tightly Packed (NO PADDING).
INSERT PADDING BYTES TO GO UP TO NEXT DWORD AT END OF ROW. SET THEM TO 0x00.
The rows are stored in reverse order (bottom to top).
16bit ... Color Data:
====================================================================================
Little Endian 2 byte RGB values. 5 bits each with MSB = 0.
Tightly Packed (NO PADDING).
INSERT PADDING BYTES TO GO UP TO NEXT DWORD AT END OF ROW. SET THEM TO 0x00.
The rows are stored in reverse order (bottom to top).
8bit ... Palette & Color Data:
====================================================================================
The Palette is Little Endian 4 byte RGB0 values. No alpha.
There *might* be up to 256 palette entries.
If number of colors is reported as zero, assume 256 color entires.
The Pixels are 1 byte index values.
INSERT PADDING BYTES TO GO UP TO NEXT DWORD AT END OF ROW. SET THEM TO 0x00.
The rows are stored in reverse order (bottom to top).
4bit ... Palette & Color Data:
====================================================================================
The Palette is Little Endian 4 byte RGB0 values. No alpha.
There *might* be up to 16 palette entries.
If number of colors is reported as zero, assume 16 color entires.
The Pixels are nybble-length index values.
INSERT PADDING BYTES TO GO UP TO NEXT DWORD AT END OF ROW. SET THEM TO 0x00.
The rows are stored in reverse order (bottom to top).
Mask Data:
====================================================================================
Is a string of bytes with mask bits starting at MSB and going towards LSB.
There are ((imagewidth+31)>>5) DWORDS per row in *BIG ENDIAN* order.
Like the color data, there is a set of DWORDS for each row.
The rows are stored in reverse order (bottom to top).
Set unused padding bits/pixels at end of each row to 1.
0 indicates opaque and 1 indicates transparent.
1bit ... XOR Mask, AND Mask, & Color Data:
====================================================================================
The Palette is Little Endian 4 byte RGB0 values. No alpha.
There should be exactly 2 palette entries: usually 0x00000000 and 0x00FFFFFF.
The two masks follow the Mask Data format decribed above.
The following results from combining two mask bits:
XOR AND RESULT:
0 0 Color #0 (Black)
0 1 Transparent
1 0 Color #1 (White)
1 1 Invert Destination Bitmap
Of course I wouldn't have left it at this. There's code to be had!
The following code will load up and convert all of the icon images for a given icon location to a vector of 32bpp BITMAPs. If loading a given image fails, it will simply just not be added to the vector (or, in the case of a corrupt icon, it will most likely generate a corrupted image, so be careful).
The code does not support the "invert" color in monochrome images, and will just generate a different color that still has zero alpha.
WARNING: Some psuedo-code is included to shorten things to just the essentials.
Icon Loader Code (Supports: EXE, DLL, 32bit ICL, ICO):
// Code written by Simion32.
// Please feel free to use it anywhere.
// Credit would be nice but isn't required.
#include "include.h" //std::vectors and whatever else you need
#include <allegro.h>
#include <winalleg.h> //Allegro and Win32
#include "Shellapi.h"
// In the following block, the (required!!) pragmas
// force correct data alignment. Needed in at least GCC.
#pragma pack( push, 1 )
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved ( must be 0)
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // How many bytes in this resource?
DWORD dwImageOffset; // Where in the file is this image?
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource Type (1 for icons)
WORD idCount; // How many images?
ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;
typedef struct
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
#pragma pack( pop)
#pragma pack( push, 2 )
typedef struct
{
BYTE bWidth; // Width, in pixels, of the image
BYTE bHeight; // Height, in pixels, of the image
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // total size of the RT_ICON resource referenced by the nID member.
WORD nID; // resourceID of RT_ICON (LockResource to obtain a pointer to its ICONIMAGE)
} GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
typedef struct
{
WORD idReserved; // Reserved (must be 0)
WORD idType; // Resource type (1 for icons)
WORD idCount; // How many images?
GRPICONDIRENTRY idEntries[1]; // The entries for each image
} GRPICONDIR, *LPGRPICONDIR;
#pragma pack( pop )
uint32_t Convert16BitToARGB(uint16_t value)
{
return (0xFF000000|((value >> 7) & 0x0000F8)|((value << 6) & 0x00F800)|((value << 19) & 0xF80000));
}
uint32_t GetMaskBit(uint8_t* data, int x, int y, int w, int h)
{
uint32_t mask_data_rowsize = (((w+31)>>5) * 4);
return ((~(data[(mask_data_rowsize * ((h-1)-y)) + (x >> 3)] >> (0x07 - (x & 0x07))) & 1) * 0xFFFFFFFF);
}
uint32_t GetColorMonochrome(uint8_t* xordata, uint8_t* anddata, int x, int y, int w, int h, uint32_t* pal)
{
uint32_t mask_data_rowsize = (((w+31)>>5) * 4);
uint32_t xor_bit = (((xordata[(mask_data_rowsize * ((h-1)-y)) + (x >> 3)] >> (0x07 - (x & 0x07))) << 1) & 2);
uint32_t and_bit = (((anddata[(mask_data_rowsize * ((h-1)-y)) + (x >> 3)] >> (0x07 - (x & 0x07))) ) & 1);
uint32_t value = (xor_bit | and_bit);
return pal[value];
}
BITMAP* CreateBmp32bppFromIconResData(void* data, int size, int depth, int w, int h, int colors)
{
char* pngheader = "\211PNG\r\n\032\n";
char* cpd = (char*)data;
bool is_png = ((cpd[0]==pngheader[0])
&& (cpd[1]==pngheader[1])
&& (cpd[2]==pngheader[2])
&& (cpd[3]==pngheader[3])
&& (cpd[4]==pngheader[4])
&& (cpd[5]==pngheader[5])
&& (cpd[6]==pngheader[6])
&& (cpd[7]==pngheader[7]));
if(is_png)
{
//###########################################################
//# PSEUDO-CODE: Somehow convert the PNG file into a bitmap.
BITMAP* result = ConvertPngFileToBmp32bpp(data, size);
return result;
}
else
{
uint32_t ignore_size = ((BITMAPINFOHEADER*)(data))->biSize;
BITMAP* bmp = create_bitmap_ex(32,w,h);
uint32_t pixel_count = (w * h);
uint32_t color_data_size = ((((((w * depth)+7) >> 3) +3) & ~3) * h);
switch(depth)
{
default: return bmp; break;
case 32:
{
uint32_t* src = (uint32_t*)(((uint8_t*)data) + ignore_size);
for(int yy = h-1; yy >= 0; --yy){
for(int xx = 0; xx < w; ++xx){
_putpixel32(bmp,xx,yy,src[0]);
src++;
}
//There should never be any padding to jump over here.
}
return bmp;
}
break;
case 24:
{
uint32_t* src = (uint32_t*)(((uint8_t*)data) + ignore_size);
uint8_t* bitmask = (uint8_t*)(((uint8_t*)data) + ignore_size + color_data_size);
int padding_checker = 0;
for(int yy = h-1; yy >= 0; --yy){
for(int xx = 0; xx < w; ++xx){
_putpixel32(bmp,xx,yy,((src[0] & 0x00FFFFFF) | 0xFF000000) & GetMaskBit(bitmask, xx, yy, w, h));
src++;
src = (uint32_t*)(((uint8_t*)src)-1); //go back a byte due to packing
padding_checker += 3;
padding_checker &= 3;
}
//This loop jumps over any padding bytes.
while(padding_checker)
{
src = (uint32_t*)(((uint8_t*)src)+1);
padding_checker++;
padding_checker &= 3;
}
}
return bmp;
}
break;
case 16:
{
//Note: there might be a color table present! ignore it.
uint16_t* src = (uint16_t*)(((uint8_t*)data) + ignore_size + (colors << 2));
uint8_t* bitmask = (uint8_t*)(((uint8_t*)data) + ignore_size + (colors << 2) + color_data_size);
int padding_checker = 0;
for(int yy = h-1; yy >= 0; --yy){
for(int xx = 0; xx < w; ++xx){
_putpixel32(bmp,xx,yy,Convert16BitToARGB(src[0]) & GetMaskBit(bitmask, xx, yy, w, h));
src++;
padding_checker += 2;
padding_checker &= 3;
}
//This loop jumps over any padding bytes.
while(padding_checker)
{
src = (uint16_t*)(((uint8_t*)src)+1);
padding_checker++;
padding_checker &= 3;
}
}
return bmp;
}
break;
case 8:
{
if(colors > 256) colors = 256; //Color Count must be restricted to 256 entries at the most.
if(colors <= 0) colors = 256; //Color Count might be reported as zero. This means 256.
uint8_t* src = (((uint8_t*)data) + ignore_size + (colors << 2));
uint32_t* pal = ((uint32_t*)(((uint8_t*)data) + ignore_size));
uint8_t* bitmask = (uint8_t*)(((uint8_t*)data) + ignore_size + (colors << 2) + color_data_size);
int padding_checker = 0;
for(int yy = h-1; yy >= 0; --yy){
for(int xx = 0; xx < w; ++xx){
uint8_t color = src[0];
if(color < colors){
_putpixel32(bmp,xx,yy,(pal[color] | 0xFF000000) & GetMaskBit(bitmask, xx, yy, w, h));
}else{
_putpixel32(bmp,xx,yy,0x00FF00FF);
}
src++;
padding_checker++;
padding_checker &= 3;
}
//This loop jumps over any padding bytes.
while(padding_checker)
{
src++;
padding_checker++;
padding_checker &= 3;
}
}
return bmp;
}
break;
case 4:
{
if(colors > 16) colors = 16; //Color Count must be restricted to 16 entries at the most.
if(colors <= 0) colors = 16; //Color Count might be reported as zero. This means 16.
uint8_t* src = (((uint8_t*)data) + ignore_size + (colors << 2));
uint32_t* pal = ((uint32_t*)(((uint8_t*)data) + ignore_size));
uint8_t* bitmask = (uint8_t*)(((uint8_t*)data) + ignore_size + (colors << 2) + color_data_size);
int padding_checker = 0;
for(int yy = h-1; yy >= 0; --yy){
for(int xx = 0; xx < w; ++xx){
uint8_t color = src[0];
if(xx & 1) color = ( color & 0x0F);
else color = ((color >> 4) & 0x0F);
if(color < colors){
_putpixel32(bmp,xx,yy,(pal[color] | 0xFF000000) & GetMaskBit(bitmask, xx, yy, w, h));
}else{
_putpixel32(bmp,xx,yy,0x00FF00FF);
}
if(xx & 1)
{
src++;
padding_checker++;
padding_checker &= 3;
}
}
//if the pointer hasn't incremented to the next byte yet, do so.
if(w & 1) //odd width
{
src++;
padding_checker++;
padding_checker &= 3;
}
//This loop jumps over any padding bytes.
while(padding_checker)
{
src++;
padding_checker++;
padding_checker &= 3;
}
}
return bmp;
}
break;
case 1:
{
if(colors > 2) colors = 2; //Color Count must be restricted to 2 entries at the most.
if(colors <= 0) colors = 2; //Color Count might be reported as zero. This means 2.
uint32_t* pal = (uint32_t*)(((uint8_t*)data) + ignore_size);
uint8_t* bitmaskXOR = (uint8_t*)(((uint8_t*)data) + ignore_size + (colors << 2));
uint8_t* bitmaskAND = (uint8_t*)(((uint8_t*)data) + ignore_size + (colors << 2) + color_data_size);
uint32_t ret_colors[4] = {pal[0]|0xFF000000, 0x00FF00FF, pal[1]|0xFF000000, 0x0000FF00};
for(int yy = h-1; yy >= 0; --yy){
for(int xx = 0; xx < w; ++xx){
_putpixel32(bmp,xx,yy,GetColorMonochrome(bitmaskXOR, bitmaskAND, xx, yy, w, h, ret_colors));
}
}
return bmp;
}
break;
}
return bmp;
}
}
vector< BITMAP* > ResourceToBitmapVector(HMODULE hm, HRSRC hr, bool is_group_icon)
{
vector< BITMAP* > results;
if(is_group_icon)
{
HGLOBAL hg = LoadResource(hm,hr);
GRPICONDIR* gd = (GRPICONDIR*)LockResource(hg);
if(gd->idType == 1)
{
for(int i = 0; i < gd->idCount; ++i)
{
//WARNING: The GRPICONDIRENTRY's data might be wrong!
GRPICONDIRENTRY* ie = (GRPICONDIRENTRY*)&(gd->idEntries[i]);
HRSRC ihr = FindResource(hm,MAKEINTRESOURCE(ie->nID),RT_ICON);
if(ihr != NULL)
{
HGLOBAL ihg = LoadResource(hm,ihr);
void* data = (void*)LockResource(ihg);
DWORD size = SizeofResource(hm,ihr);
uint32_t b = ((BITMAPINFOHEADER*)(data))->biBitCount;
uint32_t w = ((BITMAPINFOHEADER*)(data))->biWidth;
uint32_t h = (((BITMAPINFOHEADER*)(data))->biHeight >> 1); //icons have doubled height value.
uint32_t c = ((BITMAPINFOHEADER*)(data))->biClrUsed;
results.push_back(CreateBmp32bppFromIconResData(data, size, b, w, h, c));
}
}
}
}
else
{
HGLOBAL ihg = LoadResource(hm,hr);
void* data = (void*)LockResource(ihg);
DWORD size = SizeofResource(hm,hr);
uint32_t b = ((BITMAPINFOHEADER*)(data))->biBitCount;
uint32_t w = ((BITMAPINFOHEADER*)(data))->biWidth;
uint32_t h = (((BITMAPINFOHEADER*)(data))->biHeight >> 1); //icons have doubled height value.
uint32_t c = ((BITMAPINFOHEADER*)(data))->biClrUsed;
results.push_back(CreateBmp32bppFromIconResData(data, size, b, w, h, c));
}
return results;
}
vector< BITMAP* > IconFileToBitmapVector(void* icon_data, uint32_t icon_size)
{
vector< BITMAP* > results;
ICONDIR* gd = (ICONDIR*)icon_data;
if(gd->idType == 1)
{
for(int i = 0; i < gd->idCount; ++i)
{
//WARNING: The ICONDIRENTRY's data might be wrong!
DWORD offset = gd->idEntries[i].dwImageOffset;
DWORD size = gd->idEntries[i].dwBytesInRes;
void* data = (void*)(((uint8_t*)icon_data) + ((uint32_t)offset));
uint32_t b = ((BITMAPINFOHEADER*)(data))->biBitCount;
uint32_t w = ((BITMAPINFOHEADER*)(data))->biWidth;
uint32_t h = (((BITMAPINFOHEADER*)(data))->biHeight >> 1); //icons have doubled height value.
uint32_t c = ((BITMAPINFOHEADER*)(data))->biClrUsed;
results.push_back(CreateBmp32bppFromIconResData(data, size, b, w, h, c));
}
}
return results;
}
vector< BITMAP* > UnearthIconResource(string& file, bool self_refrence, bool res_index, int index)
{
#define LOAD_IGNORE_CODE_AUTHZ_LEVEL 0x00000010
//prevents a negative indexing error
// (the boolean res_index handles whether it's icon index VS resource ID)
index = abs(index);
vector< BITMAP* > results; //array of results to return (pointers to 32bpp images)
//extract and 'demangle' the file extension by convertng to lowercase.
string ext = get_file_extension(file.c_str());
for(int i = 0; i < ext.size(); ++i) ext[i] = tolower(ext[i]);
bool is_icl = false;
if((ext == "exe") || (ext == "dll") || (ext == "scr") || (is_icl = (ext == "icl")))
{
// Portable Executable Resource (works for both DLL and EXE)
// Also works for any 32bit Icon Library (Microangelo Studio?)
HMODULE hm = LoadLibraryEx(file.c_str(), NULL,
(DONT_RESOLVE_DLL_REFERENCES | LOAD_IGNORE_CODE_AUTHZ_LEVEL | LOAD_LIBRARY_AS_DATAFILE));
if(hm != NULL)
{
HRSRC hr;
if(!self_refrence)
{
if(res_index)
{
//The icon we want is at the resource ID (==index)
bool is_single_icon = false;
hr = FindResource(hm,MAKEINTRESOURCE(index),RT_GROUP_ICON);
if(hr == NULL)
{
hr = FindResource(hm,MAKEINTRESOURCE(index),RT_ICON);
is_single_icon = (hr != NULL);
}
if(hr != NULL)
{
results = ResourceToBitmapVector(hm, hr, !is_single_icon);
}
}
else
{
//The icon we want is the (index)'th icon in the file
//We must preform a manual search for the resource ID!
//WARNING: Using EnumResourceNames() *DOES NOT WORK PROPERLY* for this.
for(int nicon = 0, i = 0; i < 0x8000; ++i)
{
bool is_single_icon = false;
hr = FindResource(hm,MAKEINTRESOURCE(i),RT_GROUP_ICON);
if(hr != NULL)
{
if(nicon == index)
{
results = ResourceToBitmapVector(hm, hr, true);
break;
}
nicon++;
}
}
}
}
else
{
//The icon we want is the "first" icon in the file.
//Happens when location is a %1.
//We must preform a manual search for the resource ID!
//WARNING: Using EnumResourceNames() *DOES NOT WORK PROPERLY* for this.
for(int i = 0; i < 0x8000; ++i)
{
bool is_single_icon = false;
hr = FindResource(hm,MAKEINTRESOURCE(i),RT_GROUP_ICON);
if(hr != NULL)
{
results = ResourceToBitmapVector(hm, hr, true);
break;
}
}
}
FreeLibrary(hm);
}
else /*if(is_icl)
{//OH NOES. We have to load a *16bit* .icl file!
//not supported yet. sorry. left as another excecise to the programmer.
}*/
}
else if(ext == "ico")
{
//Single Icon File
//###################################################
//# PSEUDO-CODE: Do the file loading yourself ;)
void* data_pointer = NULL;
uint32_t data_size = 0;
if(data_pointer = MyLoadFile(file.c_str(), &data_size))
{
if(data_size)
{
results = IconFileToBitmapVector((void*)data_pointer, data_size);
}
}
MyCloseFile(data_pointer);
}
return results;
}
I think that almost covers it all...
One last thing I should mention: Be sure to ignore the size and bit depth information coming from the icon directory entries. They can often be wrong. I've seen a few 256-color images reported as 24bit, causing data corruption inside the image loader.
Wow, talk about reinventing the wheel!
With all due respect, this code is so bloated for nothing. I (and probably thousands of others) achieved the exact same result with 1/10 of this code. Also, this solution contains many inaccuracies.
Here's a quick run-down:
Why parse the registry manually? You state the API has some problems; like what? I've used reg parsing API extensively and never had a problem! The Indexing vs ResID logic is correct though.
Why do all the icon to bitmap conversions manually? This can be achieved with 3 to 5 lines of code using the right Icon API calls. Here's a complete reference.
Why limit the conversion to 32bpp? Again, using the right APIs will generate a device dependent hIcon handle with the max color bit-depth supported by that device. Check out the CreateIconFromResourceEx() API function. All you need to do is combine it with the Find/Load/Lock Resource APIs that you're already using. Using this technique will load icons of any size and color depth (from monochrome up to alpha-channel 32bpp icons).
Finally, regarding the search for icon resources by group (RT_GROUP_ICON), or by single icons (RT_ICON), and matching for a given index instead of resource, it could be done much more efficiently using EnumResourceNames(). It might be that you've failed to account for string resource identifiers when parsing the Enum return, because it seems you've omitted such case in your manual search and match procedure. This might be the source of your problems with EnumResourceNames(). It works perfectly fine for me and for others in countless online samples. At the very least, the "manual" search should match up to 0xFFFF rather than 0x8000. Res IDs are recommended in the 0x0001 to 0x8000 range, but legal in the 0x0000 to 0xFFFF range.
If it does have not to be platform independent:
a bit time ago i wrote a little class that reads a file and extract all icons.
It retreives a std::vector with HICONs.
With GetIconInfo you can retreive the HBITMAP for pixeldata an pixelmask.
The function is a little bit heuristic. It scans the binary Data for a typical icon begin and tries to load them.
The function also works on dlls, exe or icl (16bit dlls that just contain icon resources)
#ifndef __ICON_LIST_H__
#define __ICON_LIST_H__
#include <windows.h>
#include <vector>
class IconFile: public std::vector<HICON>{
public:
IconFile(){};
IconFile(std::string i_filename){
addIconsFromFile(i_filename);
};
int addIconsFromFile(std::string i_fileName){
int iCount=0;
HANDLE file = CreateFile( i_fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if(file!=INVALID_HANDLE_VALUE){
int size = GetFileSize(file,NULL);
DWORD actRead;
BYTE* buffer = new BYTE[size];
ReadFile(file, buffer, size, &actRead, NULL);
CloseHandle(file);
int ind = -1;
for(int p = 0; p< size-4; ++p){
if(buffer[p]==40 && buffer[p+1]==0 && buffer[p+2]==0 && buffer[p+3]==0){
HICON icon = CreateIconFromResourceEx(&buffer[p], size-p, true, 0x00030000,0,0,0);
if(icon){
++iCount;
this->push_back(icon);
}
}
}
delete[] buffer;
}
return iCount;
};
};
#endif //__ICON_LIST_H__