When you store a file in OneDrive, an :ms-properties alternate data stream is added. I opened an example stream using FlexHex (as shown in the image), but I can't tell what type of structure those bytes might represent. Does anyone know?
Actually, based on the 1SPS sequence, I think it might be a prop store or a shell bag or something. For reference. And this. But I'm not sure if that's right.
They are just serialized Windows properties. You can write and read these files (as streams) using builtin Windows implementation of IPropertyStore, for example using the PSCreateMemoryPropertyStore function
Here is a small sample console app that creates a test.props file with one property of string type:
#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <propsys.h>
#include <propkey.h>
#include <propvarutil.h>
// some COM error handling useful macros
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define SBTRACE wprintf
#define CHECKHR(expr) {hr=(expr);if(FAILED(hr)){ SBTRACE(L"HR FAILED line:%u file:%s\n", __LINE__, __WFILE__); goto cleanup; } }
#define HR HRESULT hr=S_OK;
int main()
{
HR;
PROPVARIANT pv;
PropVariantInit(&pv);
CoInitialize(NULL);
{
CComPtr<IPropertyStore> ps;
CComPtr<IPersistStream> pstream;
CComPtr<IStream> stream;
// create the in-memory store
CHECKHR(PSCreateMemoryPropertyStore(IID_PPV_ARGS(&ps)));
// define some PROPVARIANT value (here it's a string)
CHECKHR(InitPropVariantFromString(L"hello world", &pv));
// any property key would work
CHECKHR(ps->SetValue(PKEY_ItemNameDisplay, pv));
// get IPersistStream to be able to load or write
CHECKHR(ps->QueryInterface(&pstream));
// create a file stream
CHECKHR(SHCreateStreamOnFileEx(L"test.props", STGM_WRITE | STGM_CREATE, 0, TRUE, nullptr, &stream));
// this sample only saves, but you can load from an existing stream
CHECKHR(pstream->Save(stream, TRUE));
}
cleanup:
PropVariantClear(&pv);
CoUninitialize();
return 0;
}
Here is the result:
1SPS is the signature for a serialized property store, which is essentially a key value pair type system. its a standard structure, so its easy to parse, although the data types can make it a bit of a challenge.
It looks like there are some GUIDs in there among the 4 or so. it would be easy enough to parse out those structures as similar things are used in shellbags. it certainly just looks like a series of 1sps blocks which makes it easy.
you already know my email, so if you can extract out a few of these ADS examples, zip them, and send, i can take a closer look. if its warranted, ill even write a new forensic tool to parse them
Related
Ordinarily XInput controllers are identified simply using an index corresponding to the player number of the controller. Is there a way to obtain more information about a controller with a specific index, such as its vendor ID, product ID, or device name?
Even better would be a identifier that corresponds uniquely and consistently to just that controller so that it can be distinguished from all other XInput devices regardless of its index, including another controller that's an identical model (i.e. same product and vendor ID), similar to the instance GUID available using DirectInput.
Can this be accomplished using XInput or another Microsoft API? I'm also open to using undocumented functions if need be.
There are a few undocumented functions inside the XInput1_4.dll. You can get the Vendor ID and Product ID like this:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Xinput.h>
#include <stdio.h>
struct XINPUT_CAPABILITIES_EX
{
XINPUT_CAPABILITIES Capabilities;
WORD vendorId;
WORD productId;
WORD revisionId;
DWORD a4; //unknown
};
typedef DWORD(_stdcall* _XInputGetCapabilitiesEx)(DWORD a1, DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES_EX* pCapabilities);
_XInputGetCapabilitiesEx XInputGetCapabilitiesEx;
void main()
{
HMODULE moduleHandle = LoadLibrary(TEXT("XInput1_4.dll"));
XInputGetCapabilitiesEx = (_XInputGetCapabilitiesEx)GetProcAddress(moduleHandle, (char*)108);
for (int i = 0; i < 4; ++i)
{
printf("Gamepad %d ", i);
XINPUT_CAPABILITIES_EX capsEx;
if (XInputGetCapabilitiesEx(1, i, 0, &capsEx) == ERROR_SUCCESS)
{
printf("connected, vid = 0x%04X pid = 0x%04X\n", (int)capsEx.vendorId, (int)capsEx.productId);
}
else
{
printf("not connected\n");
}
}
}
What XInput internally does is open a device, then call DeviceIoControl on it every time it reads the joypad. (control code 0x8000e00c)
You need to hook these functions imported by "XInput1_4.dll":
CreateFileW from "api-ms-win-core-file-l1-1-0.dll"
DuplicateHandle from "api-ms-win-core-handle-l1-1-0.dll"
CloseHandle from "api-ms-win-core-handle-l1-1-0.dll"
DeviceIoControl from "api-ms-win-core-io-l1-1-0.dll"
Using the hooks for CreateFileW, DuplicateHandle and CloseHandle, you can keep track of what filename is associated with a handle.
Then when you see a call to DeviceIoControl with control code 0x8000e00c, you will know what filename is being read.
The first time you call XInputGetState, it will open multiple devices, and call DeviceIoControl multiple times, regardless of what player number you have asked for. You are only interested in the last filename seen by DeviceIoControl before XInputGetState returns. And if XInputGetState indicates the controller is not plugged in, disregard the filename you have collected for that controller number.
Examples of filenames I have seen on my own computer:
\\?\hid#{00001124-0000-1000-8000-00805f9b34fb}&vid_045e&pid_02e0&ig_00#8&7074921&2&0000#{ec87f1e3-c13b-4100-b5f7-8b84d54260cb}
\\?\usb#vid_045e&pid_028e#1&1a590e2c&1&01#{ec87f1e3-c13b-4100-b5f7-8b84d54260cb}
edit:
One more hook is required as well.
CoCreateInstance from "api-ms-win-core-com-l1-1-0.dll", to hook creating the undocumented IDeviceBroker COM object. If it can successfully create an IDeviceBroker COM object, it will use that instead of the call to CreateFileW. Parameters will be: CLSID_DeviceBroker = {acc56a05-e277-4b1e-a43e-7a73e3cd6e6c}, IID_IDeviceBroker = {8604b268-34a6-4b1a-a59f-cdbd8379fd98}. The method OpenDeviceFromInterfacePath will be called instead of CreateFileW. Alternatively, you can make creating the IDeviceBroker object simply fail, and it will proceed to use CreateFileW as usual.
So my problem sounds like this.
I have some platform dependent code (embedded system) which writes to some MMIO locations that are hardcoded at specific addresses.
I compile this code with some management code inside a standard executable (mainly for testing) but also for simulation (because it takes longer to find basic bugs inside the actual HW platform).
To alleviate the hardcoded pointers, i just redefine them to some variables inside the memory pool. And this works really well.
The problem is that there is specific hardware behavior on some of the MMIO locations (w1c for example) which makes "correct" testing hard to impossible.
These are the solutions i thought of:
1 - Somehow redefine the accesses to those registers and try to insert some immediate function to simulate the dynamic behavior. This is not really usable since there are various ways to write to the MMIO locations (pointers and stuff).
2 - Somehow leave the addresses hardcoded and trap the illegal access through a seg fault, find the location that triggered, extract exactly where the access was made, handle and return. I am not really sure how this would work (and even if it's possible).
3 - Use some sort of emulation. This will surely work, but it will void the whole purpose of running fast and native on a standard computer.
4 - Virtualization ?? Probably will take a lot of time to implement. Not really sure if the gain is justifiable.
Does anyone have any idea if this can be accomplished without going too deep? Maybe is there a way to manipulate the compiler in some way to define a memory area for which every access will generate a callback. Not really an expert in x86/gcc stuff.
Edit: It seems that it's not really possible to do this in a platform independent way, and since it will be only windows, i will use the available API (which seems to work as expected). Found this Q here:
Is set single step trap available on win 7?
I will put the whole "simulated" register file inside a number of pages, guard them, and trigger a callback from which i will extract all the necessary info, do my stuff then continue execution.
Thanks all for responding.
I think #2 is the best approach. I routinely use approach #4, but I use it to test code that is running in the kernel, so I need a layer below the kernel to trap and emulate the accesses. Since you have already put your code into a user-mode application, #2 should be simpler.
The answers to this question may provide help in implementing #2. How to write a signal handler to catch SIGSEGV?
What you really want to do, though, is to emulate the memory access and then have the segv handler return to the instruction after the access. This sample code works on Linux. I'm not sure if the behavior it is taking advantage of is undefined, though.
#include <stdint.h>
#include <stdio.h>
#include <signal.h>
#define REG_ADDR ((volatile uint32_t *)0x12340000f000ULL)
static uint32_t read_reg(volatile uint32_t *reg_addr)
{
uint32_t r;
asm("mov (%1), %0" : "=a"(r) : "r"(reg_addr));
return r;
}
static void segv_handler(int, siginfo_t *, void *);
int main()
{
struct sigaction action = { 0, };
action.sa_sigaction = segv_handler;
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, NULL);
// force sigsegv
uint32_t a = read_reg(REG_ADDR);
printf("after segv, a = %d\n", a);
return 0;
}
static void segv_handler(int, siginfo_t *info, void *ucontext_arg)
{
ucontext_t *ucontext = static_cast<ucontext_t *>(ucontext_arg);
ucontext->uc_mcontext.gregs[REG_RAX] = 1234;
ucontext->uc_mcontext.gregs[REG_RIP] += 2;
}
The code to read the register is written in assembly to ensure that both the destination register and the length of the instruction are known.
This is how the Windows version of prl's answer could look like:
#include <stdint.h>
#include <stdio.h>
#include <windows.h>
#define REG_ADDR ((volatile uint32_t *)0x12340000f000ULL)
static uint32_t read_reg(volatile uint32_t *reg_addr)
{
uint32_t r;
asm("mov (%1), %0" : "=a"(r) : "r"(reg_addr));
return r;
}
static LONG WINAPI segv_handler(EXCEPTION_POINTERS *);
int main()
{
SetUnhandledExceptionFilter(segv_handler);
// force sigsegv
uint32_t a = read_reg(REG_ADDR);
printf("after segv, a = %d\n", a);
return 0;
}
static LONG WINAPI segv_handler(EXCEPTION_POINTERS *ep)
{
// only handle read access violation of REG_ADDR
if (ep->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION ||
ep->ExceptionRecord->ExceptionInformation[0] != 0 ||
ep->ExceptionRecord->ExceptionInformation[1] != (ULONG_PTR)REG_ADDR)
return EXCEPTION_CONTINUE_SEARCH;
ep->ContextRecord->Rax = 1234;
ep->ContextRecord->Rip += 2;
return EXCEPTION_CONTINUE_EXECUTION;
}
So, the solution (code snippet) is as follows:
First of all, i have a variable:
__attribute__ ((aligned (4096))) int g_test;
Second, inside my main function, i do the following:
AddVectoredExceptionHandler(1, VectoredHandler);
DWORD old;
VirtualProtect(&g_test, 4096, PAGE_READWRITE | PAGE_GUARD, &old);
The handler looks like this:
LONG WINAPI VectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
static DWORD last_addr;
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) {
last_addr = ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
ExceptionInfo->ContextRecord->EFlags |= 0x100; /* Single step to trigger the next one */
return EXCEPTION_CONTINUE_EXECUTION;
}
if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) {
DWORD old;
VirtualProtect((PVOID)(last_addr & ~PAGE_MASK), 4096, PAGE_READWRITE | PAGE_GUARD, &old);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
This is only a basic skeleton for the functionality. Basically I guard the page on which the variable resides, i have some linked lists in which i hold pointers to the function and values for the address in question. I check that the fault generating address is inside my list then i trigger the callback.
On first guard hit, the page protection will be disabled by the system, but i can call my PRE_WRITE callback where i can save the variable state. Because a single step is issued through the EFlags, it will be followed immediately by a single step exception (which means that the variable was written), and i can trigger a WRITE callback. All the data required for the operation is contained inside the ExceptionInformation array.
When someone tries to write to that variable:
*(int *)&g_test = 1;
A PRE_WRITE followed by a WRITE will be triggered,
When i do:
int x = *(int *)&g_test;
A READ will be issued.
In this way i can manipulate the data flow in a way that does not require modifications of the original source code.
Note: This is intended to be used as part of a test framework and any penalty hit is deemed acceptable.
For example, W1C (Write 1 to clear) operation can be accomplished:
void MYREG_hook(reg_cbk_t type)
{
/** We need to save the pre-write state
* This is safe since we are assured to be called with
* both PRE_WRITE and WRITE in the correct order
*/
static int pre;
switch (type) {
case REG_READ: /* Called pre-read */
break;
case REG_PRE_WRITE: /* Called pre-write */
pre = g_test;
break;
case REG_WRITE: /* Called after write */
g_test = pre & ~g_test; /* W1C */
break;
default:
break;
}
}
This was possible also with seg-faults on illegal addresses, but i had to issue one for each R/W, and keep track of a "virtual register file" so a bigger penalty hit. In this way i can only guard specific areas of memory or none, depending on the registered monitors.
Moving from Boost 1.54 to 1.55 I now get this error during compilation (VS2010):
void GzipDecompression::Decompress(const unsigned char * src, int length)
{
if(src)
{
// Create an input-stream source for the data buffer so we can used the boost filtering buffer
std::ifstream inputstream;
typedef boost::iostreams::basic_array_source<char> Device;
boost::iostreams::stream_buffer<Device> buffer((char *)src, length);
// Inflate using the GZIP filter
filtering_streambuf<input> in;
in.push(gzip_decompressor());
in.push(buffer);
// Get the result into a vector
boost::interprocess::basic_vectorstream<std::vector<char>> vectorStream;
copy(in, vectorStream);
std::vector<char> output(vectorStream.vector());
}
}
error C2243: 'type cast' : conversion from 'boost::interprocess::basic_vectorstream<CharVector> *' to 'volatile const std::basic_streambuf<_Elem,_Traits> *' exists, but is inaccessible c:\boost\boost_1_55_0\boost\iostreams\traits.hpp 57 1
It appears this is now failing:
boost::interprocess::basic_vectorstream<std::vector<char>> vectorStream;
What has changed so this doesn't compile?
Update After Reply: I've tried changing the output to this:
std::istream instream(&in);
typedef std::istream_iterator<char> istream_iterator;
typedef std::ostream_iterator<char> ostream_iterator;
std::vector<char> output;
std::copy(istream_iterator(instream), istream_iterator(), std::back_inserter(output));
But the output is different. Do I have to flush the stream or something?
Update2: Apparently the istream_iterator strips CR LF etc. Here is my working function
void GzipDecompression::Decompress(const unsigned char * src, int length)
{
if(src)
{
// Create an input-stream source for the data buffer so we can used the boost filtering buffer
std::ifstream inputstream;
typedef boost::iostreams::basic_array_source<char> Device;
boost::iostreams::stream_buffer<Device> buffer((char *)src, length);
// Inflate using the GZIP filter
filtering_streambuf<input> in;
in.push(gzip_decompressor());
in.push(buffer);
// Get the result into a vector
std::istream instream(&in);
typedef std::istreambuf_iterator<char> istreambuf_iterator;
std::vector<char> output;
std::copy(istreambuf_iterator(instream), istreambuf_iterator(), std::back_inserter(output));
}
}
Thanks
Of course the error doesn't emerge from the line you mention. Instead, it's generated deep in the template instantiations for copy_impl. The problem seems to be that Boost Iostreams tries to be smart about detecting when people use "raw buffers" as devices/streams.
The problem with that is that the Interprocess stream implementation (privately) inherits its buffer class and as such, this confuses the detection because a conversion to base seems to be available but not accessible.
This can be reproduced in GCC as well as VS2013 update 4 and all using Boost 1_58_0 as well. As such it is an error that can be reported to the Boost developers. I'd suggest it is a weakness in the Boost Interprocess implementation, although Boost Iostreams devs might be interested in making their overload selection more robust in the presence of private base classes...
In the mean time, consider using a simple boost::iostreams::array_sink or boost::iostreams::back_insert_device (IIRC) which is pretty much guaranteed to play well with Boost Iostreams, and achieves the same goals.
This question is specific to Visual C++ (you may assume Visual C++ 2005 and later).
I would like to create glue code for a program from unixoid systems (FreeBSD in particular) in order build and run on Win32 with a minimum of changes to the original .c file. Now, most of this is straightforward, but now I ran into an issue. I am using the tchar.h header and TCHAR/_TCHAR and need to create glue code for the err and errx calls (see err(3)) in the original code. Bear with me, even if you don't agree that code using tchar.h should still be written.
The calls to err and errx take roughly two forms, and this is where the problem occurs:
err(1, "some string with %d format specifiers and %s such", ...)
/* or */
err(1, NULL, ...)
The latter would output the error stored in errno (using strerror).
Now, the question is, is there any way to write a generic macro that can take both NULL and a string literal? I do not have to (and will not) care about variables getting passed as the second parameter, only NULL and literal strings.
Of course my naive initial approach didn't account for fmt passed as NULL (using variadic macros!):
#define err(eval, fmt, ...) my_err(eval, _T(fmt), __VA_ARGS__)
Right now I don't have any ideas how to achieve this with macros, because it would require a kind of mix of compile-time and runtime conditionals that I cannot imagine at the moment. So I am looking for an authoritative answer whether this is conceivable at all or not.
The method I am resorting to right now - lacking a better approach - is to write a wrapper function that accepts, just like err and errx, a (const) char * and then converting that to wchar_t * if compiled with _UNICODE defined. This should work, assuming that the caller passes a _TCHAR* string for the variable arguments after the fmt (which is a sane assumption in my context). Otherwise I'd also have to convert %s to %hs inside the format string, to handle "ANSI" strings, as MS calls them.
Here's one solution:
#define _WIN32_WINNT 0x0502
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#ifdef _UNICODE
#define LNULL NULL
#define fn(x) myfn(L ## x)
#else
#define fn(x) myfn(x)
#endif
void myfn(TCHAR * str)
{
if (str == NULL) _tprintf(_T("<NULL>")); else _tprintf(str);
_tprintf(_T("\n"));
}
int main(int argc, char ** argv)
{
fn("hello");
fn(NULL);
return 0;
}
I want more detailed information about cbClsExtra and cbWndExtra WNDCLASSEX members that are used in RegisterClassEx winapi.
MSDN says that this members can be used to set size of extra bytes that will be allocated for class and for each window instance accordingly.
MSDN says
The system initializes the bytes to zero.
1) Does this mean that bytes are initialized with zero value or system allocates zero (none) bytes by default?
2) The most important question is how to use this extra bytes (provide examples please with winapi used) and how they are used most common?
Thanks in advance.
Does this mean that bytes are initialized with zero value or system allocates zero (none) bytes by default?
initialization is always done on variables, so it means to sets the allocated extra memory to 0.
The most important question is how to use this extra bytes (provide
examples please with winapi used) and how they are used most common?
The only way to do this is via the GetClassLongPtr and GetWindowLongPtr functions, these functions are simple enough to not need examples (call Get* to get the value, and Set* set set the value, passing either the class ATOM or window HWND).
I guess this is a really old question and the person already went on with life, but I think it deserves a proper answer as I was struggling with it and the answer wasn't much help. Yes it stated how to set the extra memory and the ONLY functions to use; but much much more detail was NECESSARY.
You see, experience persons thinks things are obvious and common sense, but I beg to differ. Win32 API is not a very intuitive API. Once you learn it you get to understand certain patterns, but later discover that some parts of the API is very different from some. Example, Setting font for your window, is different from setting font in an Edit control; which is surprisingly very very different for Rich Edit Control.
Thus I always refer to MSDN documentation and when I cant get the information there; ask Stack Overflow.
///______MY SOLUTION _________
Here is how you use cbWndExtra, which is extra bytes you can allocate to each Window Instance of that class. I know not about cbClassExtra.
Note I use cbWndExtra as an alternative to GWL_USERDATA. With the latter I would create and new pointer to my special structure and set it to GWL_USERDATA. This struct has all state I need to manage the window object.
However I have been trying out cbWndExtra to avoid creating memory on the heap. For simple primitive variables.
Step 1. Create windowProc.def file. This contains enumerations and functions for accessing the window bytes in a type safe way.
#include <windows.h>
#define LINE_NUM_VIEW_WIDTH 0
#define CODE_EDITOR_EDITOR 0
#define CODE_EDITOR_LINE_VIEW (CODE_EDITOR_EDITOR + sizeof(HWND))
#define CODE_EDITOR_HEIGHT (CODE_EDITOR_LINE_VIEW + sizeof(HWND))
#define CODE_EDITOR_RESIZABLE (CODE_EDITOR_HEIGHT + sizeof(LONG))
#define LINE_NUMBER_VIEW_WND_EXTRA_BYTES sizeof(LONG)
#define CODE_EDITOR_WND_EXTRA_BYTES (CODE_EDITOR_RESIZABLE + sizeof(LONG))
#define getLineNumberViewWidth( hwnd) GetWindowLong(hwnd,LINE_NUM_VIEW_WIDTH)
#define setLineNumberViewWidth( hwnd, n) SetWindowLong(hwnd,LINE_NUM_VIEW_WIDTH,n)
#define getTextEditor( hwnd) ((HWND)GetWindowLongPtr(hwnd,CODE_EDITOR_EDITOR))
#define getLineNumberView( hwnd) ((HWND)GetWindowLongPtr(hwnd,CODE_EDITOR_LINE_VIEW))
#define setCodeEditorHeight(hwnd,n) SetWindowLong(hwnd,CODE_EDITOR_HEIGHT,n)
#define getCodeEditorHeight(hwnd) GetWindowLong(hwnd,CODE_EDITOR_HEIGHT)
#define isCodeEditorResizable(hwnd) GetWindowLong(hwnd,CODE_EDITOR_RESIZABLE)
#define setCodeEditorResizable(hwnd, yes) SetWindowLong(hwnd,CODE_EDITOR_RESIZABLE,yes)
Note the trick with GetWindowLong, GetWindowLongPtr. Use GetWindowLong for Long, int, bool, and the likes. Use GetWindowLongPtr for pointers. Also notice the Long in the name. the function returns sizeof(LONG) and stores sizeof(Long). And msdn states valid range is 0 to cbWndExtra - sizeof(Long). Thus even though you can allocate 1 byte of cbWndExtra, DONT! Allocate multiples of LONG. And also remember that GetWindowLongPtr stores and retrieves sizeof(LONG_PTR). valid range 0 - cbWndExtra - sizeof(LONG_PTR). LONG_PTR and LONG are different size on 64 bit windows.
It is really simple. GetWindowLong will always try retrieve Long. Thus if you allocate 12 bytes and try to retrieve index 10; that is a mistake as only 2 bytes can be retrieved. Maybe windows gives you a bly, but as far as I am concernes that is undefined behaviour. And I stay clear of undefined behaviour.
Note there is GetWindowWord as well. Never used it. There is no GetWindowByte, Short, Bool or any mechanism getting and setting 1 byte or 2 byte. Thus allocate one Long block for all your bools.
Step 2. create windowProc.cpp
#include <stdio.h>
#include "windowProc.def"
LRESULT CALLBACK windowProc(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
setCodeEditorHeight(hwnd,100); // from windowProc.def
printf("%i",getCodeEditorHeight(hwnd)); // from windowProc.def
return 0;
default: return DefWindowProc(hwnd, msg, wparam, lparam);
}
}
ATOM registerMainWindow()
{
WNDCLASSEX wincl = {0};
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.hInstance = (HINSTANCE)0x400000;
wincl.lpszClassName = "JavWindowProc";
wincl.lpfnWndProc = windowProc;
wincl.hCursor = LoadCursor(NULL,IDC_IBEAM);
wincl.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);
wincl.cbWndExtra = CODE_EDITOR_WND_EXTRA_BYTES; // Safely set the size with our enumeration from windowProc.def
return (LPCSTR)RegisterClassEx(&wincl);
}
HWND createMainWindow(const char *title,int width,int height)
{
static auto className = registerMainWindow();
return CreateWindowExA(
0, // Extended possibilites for variation
className,
title,
WS_CHILD,
0,0,width,height,
HWND_DESKTOP,
0,
(HINSTANCE)0x400000,
NULL // No Window Creation data// The alternative to cbWndExtra
);
}
Steps 3 Obviously create your main function with your message loop.
Hope this was a help to somebody.