Catching EM_SETCHARFORMAT and modifying the CHARFORMAT data in EN_PROTECTED - mfc

I am receiving a EM_CHARFORMAT message in my message handler when SetFont() is called on my custom control. But when I change the data in the CHARFORMAT structure pointed to in the LPARAM, it isn't being used.
void CMyRichEdit::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
*pResult = 1;
switch(pEP->msg)
{
case EM_SETCHARFORMAT:
{
CHARFORMAT *cf = reinterpret_cast<CHARFORMAT *>(pEP->lParam);
cf->dwEffects |= (CFE_PROTECTED | CFE_ITALIC);
cf->dwMask |= (CFM_PROTECTED | CFM_ITALIC);
*pResult = 0;
}
break;
The MSDN docs say only if the lParam value changes is it used over the original... but here lParam is a pointer to an object. How can I allocate a new object without the memory leaking?

How can I allocate a new object without the memory leaking?
I'm not sure that reallocating lParam is going to work, but since a window procedure doesn't have to worry about being called on multiple threads, you can point lParam to a static variable safely. That way you don't have to worry about freeing it.
case EM_SETCHARFORMAT:
{
static CHARFORMAT mycf;
CHARFORMAT *cf = reinterpret_cast<CHARFORMAT *>(pEP->lParam);
mycf = *cf;
pEP->lParam = (LPARAM)(LONG_PTR)&mycf;
cf->dwEffects |= (CFE_PROTECTED | CFE_ITALIC);
cf->dwMask |= (CFM_PROTECTED | CFM_ITALIC);
*pResult = 0;
}
break;
However, I really don't think that this is going to solve your problem.

Related

follow official direct2d sample but got access violation error [duplicate]

This question already has answers here:
Direct2D : Unhandled Exception In WM_RESIZE switch case
(2 answers)
Closed 3 years ago.
Following the official tutorial of Direct2D (https://learn.microsoft.com/en-us/windows/win32/direct2d/direct2d-quickstart) to create a sample project with Visual Studio 2019. When running the code in x86, everything works fine while changing the platform to x64, I get an error that says: 'Exception thrown: read access violation.' in SampleD2D.cpp. (the line was commented in the code below)
the error is :
Exception thrown: read access violation.
this was 0xBB18F6E8.
LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
DemoApp* pDemoApp = (DemoApp*)pcs->lpCreateParams;
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
PtrToUlong(pDemoApp)
);
result = 1;
}
else
{
DemoApp* pDemoApp = reinterpret_cast<DemoApp*>(static_cast<LONG_PTR>(
::GetWindowLongPtrW(
hwnd,
GWLP_USERDATA
)));
bool wasHandled = false;
if (pDemoApp)
{
switch (message)
{
case WM_SIZE:
{
UINT width = LOWORD(lParam);
UINT height = HIWORD(lParam);
pDemoApp->OnResize(width, height); // throw the error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
result = 0;
wasHandled = true;
break;
case WM_DISPLAYCHANGE:
{
InvalidateRect(hwnd, NULL, FALSE);
}
result = 0;
wasHandled = true;
break;
case WM_PAINT:
{
pDemoApp->OnRender();
ValidateRect(hwnd, NULL);
}
result = 0;
wasHandled = true;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
result = 1;
wasHandled = true;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
}
return result;
}
Unfortunately, I'm no WinAPI expert but, out of curiosity, I googled a bit. Now, I'm quite sure about OPs problem:
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
PtrToUlong(pDemoApp)
);
specifically PtrToUlong(pDemoApp).
That might work for 32 bit applications but not for 64 bit.
long is in MS VC++ 32 bit – for x86 as well as x64 platform.
Hence, converting a pointer to long or unsigned long is good for making it wrong on x64 (as soon as the upper 32 bits are not 0 – which is probably hard to predict).
Googling into this direction I found e.g. PtrToUlong Q/A on gamedev.net with this (old) answer:
msdn, try to avoid using these because you are casting a pointer into an unsigned long. This may work correctly on 32-bit executables but if you compile in 64-bit you may have problems.
which supports my doubts.
According to MS doc. SetWindowLongPtrW function, the signature is:
LONG_PTR SetWindowLongPtrW(
HWND hWnd,
int nIndex,
LONG_PTR dwNewLong
);
So, this should fix it:
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(pDemoApp)
);
Please, note the MS doc. about LONG_PTR:
LONG_PTR
A signed long type for pointer precision. Use when casting a pointer to a long to perform pointer arithmetic.
This type is declared in BaseTsd.h as follows:
C++
#if defined(_WIN64)
typedef __int64 LONG_PTR;
#else
typedef long LONG_PTR;
#endif
Btw. I didn't understand as well
DemoApp* pDemoApp = reinterpret_cast<DemoApp*>(static_cast<LONG_PTR>(
::GetWindowLongPtrW(
hwnd,
GWLP_USERDATA
)));
According to doc. GetWindowLongPtrW function, the function returns LONG_PTR. So, why the static_cast<LONG_PTR>? A type cast should be always the last resort if absolutely necessary. (Although, I admit that WinAPI is probably unusable without.)

Properly handle WM_PASTE in subclass procedure

RELEVANT INFORMATION:
I have a subclass procedure that needs to validate the content of the clipboard before it gets pasted.
I have managed to get the content of the clipboard successfully, at least I think so.
QUESTION:
I do not know how to construct the following if statement ( the following is pseudo code ):
if( clipboard content is OK )
defaul handler;
else
discard message;
MY EFFORTS TO SOLVE THIS:
So far this is what I have in mind:
LRESULT CALLBACK Decimalni( HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch (message)
{
case WM_PASTE:
{
bool IsTextValid = true; // indicates validity of text
if( OpenClipboard(hwnd) ) // open clipboard
{
HANDLE hClipboardData;
// get clipboard data
if( hClipboardData = GetClipboardData(CF_UNICODETEXT) )
{
// Call GlobalLock so that to retrieve a pointer
// to the data associated with the handle returned
// from GetClipboardData.
wchar_t *pchData = (wchar_t*)GlobalLock(hClipboardData);
// copy clipboard data so we can free clipboard
wchar_t result[10]; // I just need first 9 characters
memset( result, L'0', sizeof(result) );
// copy clipboard data WITH TRUNCATION!!!
wcsncpy_s( result, 10, pchData, _TRUNCATE );
// Unlock the global memory.
GlobalUnlock(hClipboardData);
/*** parse the text for validity ****/
// code for parsing text
// update IsTextValid to indicate success or fail
/*** end of parsing *******/
}
// Finally, when finished I simply close the Clipboard
// which has the effect of unlocking it so that other
// applications can examine or modify its contents.
CloseClipboard();
}
// here should be the problematic if statement
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
return FALSE;
}
break;
case WM_CHAR:
{
// filter out some invalid keys
}
break;
case WM_NCDESTROY:
::RemoveWindowSubclass( hwnd, Decimalni, 0 ); // remove subclassing
break;
}
return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
Is my idea correct or is there another way to form my if statement?
Thank you.
Best regards.
The code seems plausible, down to the action taken. Bit clunky, but that's Windows API. There may be better ways, but this should work.
One mistake: if the text is OK, you should call DefSubclassProc, not the default window procedure.
If the text is not OK you could consider emptying the clipboard. There is not enough here about your other requirements to talk about that.

Using DirectInput to receive signal after plugging in joystick

I have a C++ program that enumerates all the input devices (using direct input) at the start of the program. If the program is started, and then I plug in another controller, this controller won't be recognized until the program is restarted. Anyone know of an event I can use that will cause my program to enumerate all of the devices after a new one is plugged in?
This article discusses how to detect game pad changes. First of all, you can handle the WM_DEVICECHANGE message and check wParam for DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE. It seems that in order to receive these as WPARAMs, though, you need to call RegisterDeviceNotification first.
The article's example of how to do this is as follows:
DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
ZeroMemory(&notificationFilter, sizeof(notificationFilter));
notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
notificationFilter.dbcc_size = sizeof(notificationFilter);
HDEVNOTIFY hDevNotify;
hDevNotify = RegisterDeviceNotification(m_hWnd, &notificationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE |
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
if(hDevNotify == NULL) {
// do some error handling
}
The only other thing to watch out for is that the minimum supported OS for this is XP, so you need to put in the appropriate #define for that before including the Windows headers.
Depending on what you want to do, you might not even have to call this function first. Instead, you can just check DBT_DEVNODES_CHANGED to not differentiate between a device being plugged or unplugged. That could save some code if you don't care.
Got it working. When any device is removed or added just dispose all 'IDirectInputDevice8' and re-create them. This avoids bugs and keeps things simple.
Hook WinProc method to watch for add/remove events
bool refreshInputDevices = false;
LRESULT SubWndProc(int code, WPARAM wParam, LPARAM lParam)
{
// invalid code skip
if (code < 0) return CallNextHookEx(NULL, code, wParam, lParam);
// check if device was added/removed
PCWPSTRUCT pMsg = PCWPSTRUCT(lParam);
if (pMsg->message == WM_DEVICECHANGE)
{
switch (pMsg->wParam)
{
case DBT_DEVNODES_CHANGED:
refreshInputDevices = true;
break;
case DBT_DEVICEARRIVAL:
refreshInputDevices = true;
break;
case DBT_DEVICEREMOVECOMPLETE:
refreshInputDevices = true;
break;
}
}
// continue as normal
return CallNextHookEx(NULL, code, wParam, lParam);
}
Here is how you can hook on the input thread
// hook WinProc to watch for device changes
HMODULE module = GetModuleHandleW(NULL);
DWORD threadID = GetCurrentThreadId();
HHOOK hook = SetWindowsHookExW(WH_CALLWNDPROC, (HOOKPROC)&SubWndProc, module, threadID);

TVITEM LPARAM to store string

I've got a treeview listing files that are dropped upon it.
When I make a new treeview item, I'd like to store the address of the file as a string in that item, and retrieve it for various nefarious purposes at a later point in time.
Looking at the TVITEM structure in Microsoft docs, apparently LPARAM is the place to store a value:
lParam
Type: LPARAM
A value to associate with the item.
So, I have gone ahead and done that:
TVITEM tvi;
tvi.mask = TVIF_TEXT;
tvi.pszText = const_cast<char *> (str0.c_str());
tvi.cchTextMax = sizeof(tvi.pszText);
tvi.lParam = (LPARAM) foo; // SETTING LPARAM HERE, foo IS A const char *
TVINSERTSTRUCT tvis;
tvis.item = tvi;
tvis.hInsertAfter = 0;
tvis.hParent = hti0;
// Send message to update tree, and return tree item.
return TreeView_InsertItem(tvw_filelist_, &tvis);
Then, when I try to retrieve my value...
HTREEITEM htiSel = TreeView_GetSelection(tvw_filelist_);
TVITEM tvItem;
tvItem.hItem = htiSel;
TreeView_GetItem(tvw_filelist_, &tvItem);
const char * info = (const char *) tvItem.lParam;
MessageBox(NULL, info, "Alert", MB_OK);
...I just get garbage, indicating my pointer went out of scope or is taking a nap or something. The size of that pointer is always 4.
Is this the right way to do what I'm trying to do? If so, what's going on?
Of course, take the time to post a question after a long time trying to figure it out, and the answer shows up in seconds.
Turns out the TVITEM mask needs to include TVIF_PARAM, similar to this question.
If I change the above code to:
tvi.mask = TVIF_TEXT | TVIF_PARAM;
it works as expected.
I'm still not sure if this is the recommended use for LPARAM, though.
struct CustomTreeData
{
LPSTR str; // or even std::string to forget about memory managment
// TODO: any other data you need
};
...
TVITEM tvi;
tvi.mask = TVIF_TEXT | TVIF_PARAM;
CustomTreeData* myDataPtr = new CustomTreeData; // the memory should be free later
myDataPtr->str = stringWhatIWant; // And don't forget to alloc memory for str!
tvi.lParam = (LPARAM) myDataPtr;
I don't check this code, but it should work. Happy coding :)

Cannot add items to Win32 List Box Control

Backstory: I'm creating an Extension for Game Maker, a popular game development suite. An extension is a DLL that adds new functions to the built in scripting language, but is written in C or Pascal or whatever. Typically, it's used to allow games to use external libraries.
In my case, I'm adding FMOD support. This isn't relevant. What's relevant is that for debugging purposes, I am also adding a dialog that I display at runtime that shows me the internal state of my library. I need help with this window. I have literally done absolutely no raw Win32 forms programming before today (.NET WinForms 4eva), so I'm probably doing something really clueless.
Anyway. I have a listbox, and I want to add things to the list box, but when I try to add them, it fails. My code:
extern DebugDialog * debugDialog;
DebugDialog::DebugDialog(HWND owner, HINSTANCE hInst) {
this->hWnd = 0;
HWND hWnd = CreateDialogParam(hInst,
MAKEINTRESOURCE(IDD_DEBUGDIALOG),
owner,
DialogProc,
reinterpret_cast<LPARAM>(this));
ShowWindow(hWnd, SW_SHOW);
}
DebugDialog::~DebugDialog(void) {
DestroyWindow(this->getHWnd());
debugDialog = NULL;
}
BOOL CALLBACK DebugDialog::DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
DebugDialog * self;
if(message == WM_INITDIALOG) {
self = reinterpret_cast<DebugDialog *>(lParam);
self->hWnd = hWnd;
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self));
} else {
self = reinterpret_cast<DebugDialog*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if(self) {
return self->HandleMessage(message, wParam, lParam);
} else {
return FALSE;
}
}
BOOL DebugDialog::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_INITDIALOG:
MessageBox(this->getHWnd(), "Okay!", "Debug", 0);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam)) {
case ID_CLOSE:
case IDOK:
case IDCANCEL:
delete this;
return TRUE;
default:
return FALSE;
}
return TRUE;
}
return false;
}
void DebugDialog::loadedSound(FMODGM_Sound * sound) {
HWND hwndList = GetDlgItem(this->getHWnd(), IDC_LIST);
LPARAM sound_text = (LPARAM)sound->file.c_str();
LRESULT lResult = SendMessage(hwndList, LB_ADDSTRING, NULL, sound_text);
SendMessage(hwndList, LB_SETITEMDATA, lResult, (LPARAM)sound);
}
DebugDialog is a simple class that wraps the window, and lets me manipulate it from the outside. Basically, at some other point, I do this:
debugWindow = new DebugDialog(owner, hInst);
And then as I execute and do interesting things, I do this:
FMODGM_Sound * sound = ...;
if(debugWindow) debugWindow->loadedSound(sound);
In loadedSound, I send a message to the list box saying "Hey, here's an item. Go ahead and make with the adding.", and it doesn't return an error. However, it also doesn't add anything to the box. It returns 0 each and every time I call it. According to the documentation, 0 means that it added an item, whose index is 0. However, that item doesn't exist.
I have a theory as to why it's not working. I have no control over the message pump that Game Maker runs, so if it's doing anything funky, I don't know about it, nor can I change it. That said, everything else about the dialog works, including moving it, clicking on my Close button, and drawing the marquee thing inside the listbox with the mouse.
Someone, please tell me what I'm doing horribly wrong :(
Edit: Someone asked about the FMODGM_Sound struct, so here it is:
struct FMODGM_Sound {
FMOD::Sound * sound;
std::vector<FMOD::Channel*> channels;
std::string file;
public:
FMODGM_Sound() {
sound = NULL;
}
};
Nothing particularly fancy.
Can you show a declaration of FMODGM_Sound structure and file field?
What happen if replace
LRESULT lResult = SendMessage(hwndList, LB_ADDSTRING, NULL, sound_text);
with ?
LRESULT lResult = SendMessage(hwndList, LB_ADDSTRING, NULL, "some constant text");
Does the your DLL compiled as Unicode version or multibytes version?
If it is Unicode, the sound_text should be an Unicode string. I guess the file is a std::string, so file.c_str() will return a multibytes string.
I had a very similar problem, which was solved. Basically, you have to pass it as a c-style string instead (str.c_str()). Though I am a complete newbie, after googling around how to use that, it worked.
Though the code I'm using serves an entirely different function than yours, maybe it will be a good example.
int i = res->getInt("ID");
std::string str = boost::lexical_cast<std::string>(i);
char *cstr = new char[10];
strcpy_s(cstr, 10, str.c_str());
SendDlgItemMessage(hwnd, IDC_lbList, LB_ADDSTRING, 0, (LPARAM)cstr);
EDIT: Wow, I did not even look at the dates. I'm a necromancer...