I have been building a simple application which requires a separate thread to run some background code in a while loop. I have a text box which I want to send messages to from the code running in the separate thread however I am unable to.
A non static member reference must be relative to a specific object. From my understanding to run an additional thread it seems that I have to make the function static. However when I try and write a message to m_Console I see the error:
A non static member reference must be relative to a specific object.
I tried initialising the object but it doesn't do anything.
CMFCApplication1Dlg obj;
obj.m_Console = "Test"
The code that uses a separate thread is:
static UINT checkSomething(LPVOID pParam);
The text box variable is:
CString m_Console;
void CMFCApplication1Dlg::OnBnClickedBtnAdd(){
m_Console = "Parser is now running..";
AfxBeginThread(checkSomething,"");
I have tried the suggestion:
UINT CMFCApplication1Dlg::checkSomething(LPVOID pParam){
CMFCApplication1Dlg* pObject = (CMFCApplication1Dlg*)pParam;
pObject->m_Console = "I am in thread";
But it throws an access violation error on: pObject->m_Console = "I am in thread";
Exception thrown at 0x0FE90DBD (mfc140ud.dll) in MFCApplication1.exe: 0xC0000005: Access violation reading location 0xFFFFFFFC.
Here is the code:
MFCApplication1Dlg.cpp
void CMFCApplication1Dlg::OnBnClickedBtnAdd(){
m_Console = "Something Parser is now running..";
AfxBeginThread(checkSomething,"");
CWnd* okbtn = GetDlgItem(IDC_BTN_ADD);
if (okbtn) {
okbtn->EnableWindow(FALSE);
}
// without UpdateData() status area will _NOT_ be updated.
UpdateData(FALSE);}
INT CMFCApplication1Dlg::checkSomething(LPVOID pParam){
CMFCApplication1Dlg* pObject = (CMFCApplication1Dlg*)pParam;
pObject->m_Console = "I am in thread";
MFCApplication1Dlg.h
public:
afx_msg void OnBnClickedBtnAdd();
static int messenger();
static UINT checkSomething(LPVOID pParam);
CString m_Console;
Textbox:
IDC_Console
Category: value
Access: public
Control type: LText
Name: m_Console
Variable type: CString
From reading everyone's comments, it seems as if this is something that I am not supposed to do in C++, question is then, what if I had a background task running a loop in in a separate thread which needed to update the status box on the UI? To me that seems a logical thing someone might wish to do but if I'm not supposed to do that then how would that be done? I am running the task in a separate thread because it uses a while loop and if I don't use a separate thread it just freezes the whole application.
You can deliver the object of CMFCApplication1Dlg as parameter for the Thread function.
e.g
UINT MyThreadProc( LPVOID pParam )
{
CMFCApplication1Dlg * pObject = (CMFCApplication1Dlg *)pParam;
pObject->m_Console = "I am in thread";
}
// .... .... ...
AfxBeginThread(MyThreadProc, this);
I hope this answer will work for you.
Changed
AfxBeginThread(checkSomething,""); to
AfxBeginThread(checkSomething,this);
as suggested by ysk silver, thanks!
I needed to add a Timer Function which I followed this article:
https://www.tutorialspoint.com/mfc/mfc_multithreading.htm
Once I did this, the UI box updates.
int currValue;int maxValue;BOOL stopNow;string output;
BEGIN_MESSAGE_MAP(CMFCApplication1Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_ERASEBKGND()
ON_WM_CTLCOLOR()
ON_WM_TIMER() //TIMER
ON_BN_CLICKED(IDC_BTN_ADD, &CMFCApplication1Dlg::OnBnClickedBtnAdd)
END_MESSAGE_MAP()
void CMFCApplication1Dlg::OnTimer(UINT_PTR nIDEvent) {
CDialogEx::OnTimer(nIDEvent);
UpdateData(FALSE);}
void CMFCApplication1Dlg::OnBnClickedBtnAdd(){
SetTimer(1234, 333, 0); // 3 times per second
m_Console = "Parser is now running..";
AfxBeginThread(checkSomething,this);
I then can change the text in the checkSomething method:
CMFCApplication1Dlg* pObject = (CMFCApplication1Dlg*)pParam;
output = "I am in thread";
pObject->messenger(output);
//OR
pObject->m_Console = "I am in thread";
And the textbox updates!
I have an issue with an MFC dialog based application build with MSVC 2013. To make the main dialog accessible also during more elaborate functions, I'm using multi-threading. A click on a button in the dialog calls a "worker function" that is worked out by another thread.
Here's an excerpt of the class:
class CpiezcamDlg : public CDialogEx
{
protected:
virtual BOOL OnInitDialog();
public:
CWinThread *m_thread1;
void StartSweepAndImageThread()
{
m_thread1 = AfxBeginThread(SweepAndImageThreadProc, this);
}
private:
static UINT SweepAndImageThreadProc(LPVOID pParam)
{
CpiezcamDlg *pThis = (CpiezcamDlg *)pParam;
UINT nRet = pThis->DoSweepAndImage();
return nRet;
}
UINT DoSweepAndImage();
UINT16 steps;
CString *imgs_name;
};
Clicking a button calls StartSweepAndImageThread which itself calls SweepAndImageThreadProc and finally DoSweepAndImage. In the function DoSweepAndImage, variables of the class are accessed (read and write). Amongst others, there is imgs_name. The usage is:
UINT CpiezcamDlg::DoSweepAndImage()
{
// ...
CString str;
str.Format(_T("Test"));
AddStageListText(str);
imgs_name[i] = str;
// ...
}
while imgs_name is initialized like
steps = 4;
imgs_name = new CString[steps];
in the OnInitDialog function.
The problem is that when pressing the mention button I receive
0xC0000005: Access violation reading location 0xFDFDFDF9.
exactly on imgs_name[i] = str;. When using a statical array, that is instead of CString *imgs_name; I define CString imgs_name[4];, everything works well. However, I very much would like to have that CString variable a dynamical one. Thanks in advance for your help!
PS: When I evaluated this in a serial way, i.e. when running the DoSweepAndImage function in the main thread, everything goes well. That's why I assume the access violation is due to the multi-threading.
#Wimmel: The loop over i in DoSweepAndImage is
for (UINT16 i = 0; i < steps; i++)
I've recently learned about opaque pointers in C++. I've started using them to hide private members that are platform specific. Such as references to definitions in <windows.h> etc.
Now, I have several systems that build off each other and need to intercommunicate. For example, Direct3D needing a window handle (HWND). I do not want to expose platform definitions to my core system, however my subsystems need to communicate that data.
I'm exposing the opaque data and allowing access through a void pointer. This allows access to all private data.
Example usage (main.cpp):
// System:: namespace is my platform specific code
System::Window window;
System::DirectX::Direct3D9 gfxcontext(window);
Window definition (System/Window.h):
class Window
{
WindowData* data; // Opaque pointer
public:
void* getHandle() const; // returns an HWND handle
Window();
~Window();
}
How to retrieve useful data (Direct3D9.cpp):
#include "Window.h"
Direct3D9::Direct3D9(const Window& _window)
{
HWND windowHandle = *(HWND*)_window.getHandle();
// [...]
pp.hDeviceWindow = windowHandle;
}
However, this code works!:
*(HWND*)_window.getHandle() = 0; // changes HWND in WindowData to NULL!
Is there a way to communicate the platform specific information between subsystems without exposing it to my independent code -and- keeping private data private?
Edit current WindowData implementation
struct Window::WindowData
{
static LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
HWND windowHandle;
WNDCLASSEX windowClass;
HINSTANCE processInstance;
};
The HWND is used by DirectX in the presentation parameters (D3DPRESENT_PARAMETERS::hDeviceWindow)
I would let getHandle (or better getWindowData return a WindowData * instead of a void *. Then let WindowData be just a forward declaration in the "System/Window.h" file.
Inside "Direct3D9", use the fully definition of WindowData, so:
HWND hwnd = _window.getWindowData()->windowHandle;
If at some later stage, you port to Linux, you can have a completely different structure inside WindowData [based on some #ifdef __WIN32/#else type of structure in the implementation side].
Define functionally what you need to do, then implement it in terms of interface. Don't expose (make public) the pointer. Thereafter, implement the interface in terms of the HWND and the platform specific API.
e.g:
struct WindowHandleImpl
{
virtual void show() = 0;
virtual void maximize() = 0;
//etc...
};
struct Win32WinHandleImpl : WindowHandleImpl
{
std::unique_ptr<HWND> handle_; //Use deleter...
virtual void show(); //In terms of HWND, using Win32 API
virtual void maximize();
};
struct XWinHandleImpl : WindowHandleImpl
{
//In terms of platform specific handle.
};
struct Window
{
void show(); //In terms of WindowHandleImpl
void maximize();//In terms of WindowHandleImpl
private:
std::unique_ptr<WindowHandleImpl> pimpl_;
};
Window::Window( const Factory& factory )
: pimpl_( factory.createWindow() )
{
}
//or
Window::Window()
: pimpl_( SystemFactory::instance().createWindow() )
{
}
You can copy the data and return a unique_ptr. Or you can just return an HWND as void* instead of HWND* since it is just a pointer anyway, although that really exploits the implementation. Keep in mind though, others can still change you window somehow over the HWND, and I guess there's not much you can do about it.
I'm trying to set up tests for a upload using the plupload queue widget.
I'm using Splinter for in-browser test, but I couldn't find a way to make it happen. Splinter has some methods to attach files, but only if it's a simple file field.
Another way would be click the button to browse the files, and choose the file... but I don't think it's possible using Splinter (or selenium), is it?
Or with drag-n-drop of the files.
Anyone has any suggestion of the best way to automatize theses tests?
Its possible to automate user actions done on PLUpload control using Selenium- WebDriver. Please find the WebDriver C# code below, which clicks on a flash button object and selects a file using keyboard events,
using System;
using System.Windows.Forms;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium;
using OpenQA.Selenium.Support;
using OpenQA.Selenium.Interactions;
using NUnit.Framework;
namespace BusinessCreation
{
class PlUpload
{
static void Main(string[] args)
{
IWebDriver driver = new FirefoxDriver();
driver.Navigate().GoToUrl("http://www.plupload.com/example_queuewidget.php");
driver.FindElement(By.XPath("//object[#data='/plupload/js/plupload.flash.swf']")).Click();
SendKeys.SendWait(#"C:\Users\Public\Pictures\Sample Pictures\Dock.jpg");
SendKeys.SendWait(#"{Enter}");
}
}
}
I had a similar problem with the PlUpload widget. Thank you to CheryJose for putting me on the right track.
First of all I had to create a small class to find out which windows were open and return them as a dictionary of windows.
public static IDictionary<string, IntPtr> GetOpenWindows()
{
IntPtr lShellWindow = GetShellWindow();
Dictionary<string, IntPtr> lWindows = new Dictionary<string, IntPtr>();
EnumWindows(delegate(IntPtr hWnd, int lParam)
{
if (hWnd == lShellWindow) return true;
if (!IsWindowVisible(hWnd)) return true;
int lLength = GetWindowTextLength(hWnd);
if (lLength == 0) return true;
StringBuilder lBuilder = new StringBuilder(lLength);
GetWindowText(hWnd, lBuilder, lLength + 1);
lWindows[lBuilder.ToString()] = hWnd;
return true;
}, 0);
return lWindows;
}
public delegate bool EnumDelegate(IntPtr hWnd, int lParam);
public delegate bool EnumedWindow(IntPtr handleWindow, ArrayList handles);
[DllImport("USER32.DLL")]
public static extern bool EnumWindows(EnumDelegate enumFunc, int lParam);
[DllImport("USER32.DLL")]
public static extern IntPtr GetShellWindow();
[DllImport("USER32.DLL")]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("USER32.DLL")]
public static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("USER32.DLL")]
public static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
In the Selenium page code I click on the button to launch the PlUpload window and put in a short wait. (This is not shown in the code)
Then I use the code below to find all open windows
IDictionary<string, IntPtr> getOpenWindows = GetOpenWindows();
Switch to the PlUpload window (the name of the window is different across browsers. Be Aware!)
IntPtr hWnd = getOpenWindows["File Upload"];
SetForegroundWindow(hWnd);
Type in the path for the file
SendKeys.SendWait(filename);
Press Enter
SendKeys.SendWait(#"{Enter}");
The PlUpload window will close so we switch back to the browser window (in this case Firefox)
hWnd = getOpenWindows["Mozilla Firefox"];
SetForegroundWindow(hWnd);
There are a couple of issues with this as the window titles are different depending on which browser is being used, so this will need to be taken into account for a complete solution. In addition, when this section of code is executing do not bring any other windows to the foreground as this window will receive the 'SendKeys' rather than the required window.
I am using CWinFormsControl to host a Windows Forms UserControl in an MFC dialog. I have set the property DoubleBufferd to true. According to the docs this results in AllPaintingInWmPaint and UserPaint to be set to true too (not sure if this matters). How can I force (or fake) the UserControl to draw its background transparent?
This is what I have set in the contructor of my UserControl:
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.DoubleBuffered = true;
I have a potential solution that may work, although I would need more information on how your animated controls work to be sure. There is one unfortunate side effect in my solution, and that is that the DoubleBuffering property only works correctly in .NET control containers. When hosted in MFC, your controls will flicker on resize and other similar display-tearing refreshes. This may cause issues with animated controls, depending on how they are performing drawing work.
To start, I first looked for issues when hosting a .NET UserControl in MFC. After quite a while of reading through the instantiation code of CWinFormsControl::CreateControl() and everything beneath, nothing out of the ordinary came up. In fact, aside from the quirks of loading managed references, the code is identical to how transparent ActiveX controls are loaded.
After learning that piece of information, I used Spy++ to look at whether the .NET control is instantiated with a windowed container. Indeed, it is. After a rather lengthy investigation, this control container appears to be controlled by an instance of a utility class, System.Windows.Forms.Control.AxSourcingSite, which has no documentation and almost no visibility. This was a little bit surprising to me, as usually it is the reverse. MFC and the lesser used WTL have great support for in-place activation, and usually controls can work with whatever the host has setup, whether windowed or not.
From here, I checked on whether this same container exists when the .NET control is hosted in a .NET control container. I assumed that perhaps the control would have its own window, without any special adapters. Turns out, I was wrong. The control works the same way as in-place non-windowed controls. This means that in order to preserve behavior, a solution must allow the regular .NET activation to proceed as normal, and when windowed, it should do something else.
A close look at the MFC hosted version reveals an off-white background drawn in by the .NET UserControl. After more spading and testing, this off-white background is definitely drawn in by a hidden layer in the window message handling chain. This means we can hack together a solution by using AllPaintingInWmPaint.
To demonstrate this, here is the source code for a UserControl that can be hosted in both .NET and the MFC managed container. This control relies on the following things to work around the transparency issues.
Add a member variable, m_ReroutePaint, to allow us to know when we need to override the default WM_PAINT behavior.
Override base.CreateParams and add the WS_EX_TRANSPARENT flag. When this property is called, set m_ReroutePaint to true. This property was not called when the Control is activated in a .NET container.
Override the WndProc() method, and patch up WM_PAINT to our liking if we are rerouting painting activities.
Use BeginPaint()/EndPaint() via Interop to setup/teardown WM_PAINT. Use the provided HDC as the initializer for a Graphics object.
Here are some caveats:
The background color of the control cannot be changed through the BackColor .NET property after the control has been instantiated. One can add workarounds for this, but to keep the sample short and simple, I left out code to do this as the intended goal is for transparent controls. However, if you start with a background color that isn't transparent, the workaround is unnecessary. I did leave code in for this case.
In attaching a HDC to a Graphics object in the WM_PAINT handler via Graphics.FromHdc(), the documentation suggests that Graphics.ReleaseHdc() should be called. However, by doing this, a GDI handle leak occurs. I have left it commented out here, but perhaps someone with GDI+ internals knowledge can figure this out.
This UserControl was created in a project named 'UserCtrlLibrary1'. The DebugPrintStyle() items may be safely removed. Also, handlers were added for resize and paint, which are both in a separate designer file, but trivial to add. AllPaintingInWmPaint should be true through the lifetime of the control.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace UserCtrlLibrary1
{
public partial class CircleControl : UserControl
{
public CircleControl()
{
InitializeComponent();
DebugPrintStyle(ControlStyles.SupportsTransparentBackColor, "initial");
DebugPrintStyle(ControlStyles.AllPaintingInWmPaint, "initial");
DebugPrintStyle(ControlStyles.UserPaint, "initial");
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
DebugPrintStyle(ControlStyles.SupportsTransparentBackColor, "current");
DebugPrintStyle(ControlStyles.AllPaintingInWmPaint, "current");
DebugPrintStyle(ControlStyles.UserPaint, "current");
}
public void DebugPrintStyle(ControlStyles cs, string prefix)
{
Debug.Print("{0}: {1}={2}", prefix, cs.ToString(), this.GetStyle(cs).ToString());
}
bool m_ReroutePaint;
const int WS_EX_TRANSPARENT = 0x0020;
protected override CreateParams CreateParams
{
get
{
if (this.BackColor == Color.Transparent)
{
m_ReroutePaint = true;
CreateParams cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT;
return cp;
}
else
{
return base.CreateParams;
}
}
}
private void CircleControl_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
using (SolidBrush b = new SolidBrush(Color.Orange))
{
g.FillEllipse(b, 0, 0, this.Width, this.Height);
}
}
private void CircleControl_Resize(object sender, EventArgs e)
{
this.Invalidate();
}
const int WM_PAINT = 0x000F;
[DllImport("user32.dll")]
static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
[DllImport("user32.dll")]
static extern bool EndPaint(IntPtr hWnd, [In] ref PAINTSTRUCT lpPaint);
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
[StructLayout(LayoutKind.Sequential)]
struct PAINTSTRUCT
{
public IntPtr hdc;
public bool fErase;
public RECT rcPaint;
public bool fRestore;
public bool fIncUpdate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] rgbReserved;
}
protected override void WndProc(ref Message m)
{
if ((m.Msg == WM_PAINT) && (m_ReroutePaint))
{
PAINTSTRUCT ps = new PAINTSTRUCT();
BeginPaint(this.Handle, out ps);
using (Graphics g = Graphics.FromHdc(ps.hdc))
{
using (PaintEventArgs e = new PaintEventArgs(g, new Rectangle(ps.rcPaint.Left, ps.rcPaint.Top, ps.rcPaint.Right - ps.rcPaint.Left, ps.rcPaint.Bottom - ps.rcPaint.Top)))
{
this.OnPaint(e);
}
// HACK: This is supposed to be required...
// but it leaks handles when called!
//g.ReleaseHdc(ps.hdc);
}
EndPaint(this.Handle, ref ps);
return;
}
base.WndProc(ref m);
}
}
}
In case anyone other than the OP would like to test this, here are the details to get this up and running in MFC. I created a MFC SDI project, without document-view architecture, with ActiveX control support. This results in generation of typical «project-name» class, ChildView class, and MainFrm classes.
Inside the ChildView.h header, add the following header material before the class (but after #pragma once). Alter the name of the .NET control library if yours is different.
#include <afxwinforms.h>
#using "UserCtrlLibrary1.dll"
using namespace UserCtrlLibrary1;
Add a member variable for the .NET control host. Arbitrarily, I placed mine under the Attributes section.
// Attributes
public:
CWinFormsControl<CircleControl> m_Circle;
Also, I added handlers for OnCreate() and OnSize(). public/protected visibility may be adjusted as you need.
// Generated message map functions
protected:
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
In ChildView.cpp, I added function bodies for all the items listed above. The message map also needs updates if you didn't use ClassWizard to add the windows message handlers.
BEGIN_MESSAGE_MAP(CChildView, CWnd)
ON_WM_PAINT()
ON_WM_CREATE()
ON_WM_SIZE()
END_MESSAGE_MAP()
void CChildView::OnPaint()
{
CPaintDC dc(this); // device context for painting
RECT rt;
this->GetClientRect(&rt);
rt.right = (rt.right + rt.left)/2;
dc.FillSolidRect(&rt, RGB(0xFF, 0xA0, 0xA0));
}
int CChildView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
RECT rt;
this->GetClientRect(&rt);
m_Circle.CreateManagedControl(WS_VISIBLE, rt, this, 1);
return 0;
}
void CChildView::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
RECT rt;
this->GetClientRect(&rt);
m_Circle.MoveWindow(rt.left, rt.top, rt.right - rt.left, (rt.bottom - rt.top)/2, TRUE);
}
These changes create an instance of the UserControl, and anchor it against the top half of the view. The OnPaint() handler draws a pink band in the left half of the view. Together, transparency should be apparent in the top left quadrant of the view.
To get the MFC project to compile and run, a copy of the UserCtrlLibrary1 output needs to be placed in the same location as the executables for UserCtrlMFCHost. Also, another copy needs to be placed in the same directory as the project source code files for the #using statement. Last, the MFC project should be modified to use the /clr compilation script. In the Configuration Properties section, General subsection, this switch is listed under Project Defaults.
One interesting thing of note, is that this allows the ^ suffix for access to managed classes. At some points in developing this solution, I debated adding methods to be called only when instantiated from MFC, but given that there are ways to detect windowed/non-windowed activation, this wasn't necessary. Other implementations may need this, though, so I feel it is good to point this out.
How to: Compile MFC and ATL code with /clr