So i'm a bit new to C++(4-6 months so far + learning from books(not teachers)) and I understand this problem is most likely a result of my misunderstandings but I still can't figure it out after googling a lot of search terms...
I also tried stackoverflow and read:
Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
Best Way to Invoke Any Cross-Threaded Code?
and I attempted to create a solution for this(which you can see below), but I failed to convert the code from c# to c++(I am not a C# user so...)
I can solve it with( from: Crossthread operation not valid... - VB.NET):
listView1->CheckForIllegalCrossThreadCalls = false;
but since it is not good practice, I am looking for an alternative.
when I attempt to call this clr code in my thread:
void CClient::add(){
lvhandle = (HWND)project1::Form1::ClrForm1->listView1->Handle.ToPointer();
/* as you can see here, I tried to figure out how to use MethodInvoker but failed...
if(project1::Form1::ClrForm1->listView1->InvokeRequired){
lvhandle = (HWND)project1::Form1::ClrForm1->listView1->Handle.ToPointer();
//project1::Form1::ClrForm1->listView1->Invoke(gcnew System::Windows::Forms::MethodInvoker(this, &annoyme));
}
else
{
lvhandle = (HWND)project1::Form1::ClrForm1->listView1->Handle.ToPointer();
}*/
item.pszText = LPSTR_TEXTCALLBACK; // Sends an LVN_GETDISPINFO message.
item.mask = LVIF_TEXT | LVIF_IMAGE |LVIF_STATE;
item.stateMask = 0;
item.iSubItem = 0;
item.state = 0;
item.pszText = L"test";
item.lParam = 0;
ListView_InsertItem(lvhandle, &item);
std::string lol = std::to_string(item.iItem);
String^ test;
test = marshal_as<String^>(lol);
project1::Form1::ClrForm1->Text = test;
}
it gives me an exception:
Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on.
ClrForm1 is defined as:
public ref class Form1 : public System::Windows::Forms::Form
{
public:
static Form1^ ClrForm1;
which I create when the entry point is triggered.
int main(array<System::String ^> ^args)
{
// Enabling Windows XP visual effects before any controls are created
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
// Create the main window and run it
Form1::ClrForm1 = gcnew Form1();
Application::Run(Form1::ClrForm1);
return 0;
}
I also confirmed the error by doing:
project1::Form1::ClrForm1->Text = "Test!0";
hopefully I detailed this enough to get a response >.<
Related
My code:
void CAssignSelectedColumnDlg::AddColumnData(CString strHeading, CStringArray* pAryStrNames, int iColumnIndex, int iCustomIndex /*-1*/, BOOL bFixedCustomType /*FALSE*/)
{
//COLUMN_DATA_S *psData = nullptr;
//psData = new COLUMN_DATA_S;
auto psData = std::make_unique<COLUMN_DATA_S>();
if (psData != nullptr)
{
// Note: we don't "own" pAryStrNames
psData->strHeading = strHeading;
psData->pAryStrNames = pAryStrNames;
psData->sActionListInfo.byColumnIndex = iColumnIndex;
psData->sActionListInfo.byCustomIndex = iCustomIndex;
psData->sActionListInfo.bFixedCustomType = bFixedCustomType ? true : false;
m_aryPtrColumnData.Add(psData);
}
}
Code analysis was suggesting I use std::make_unique instead of new. So I adjusted teh code but now get a compile error. I tried to find a suitable approach:
No suitable conversion function from std::unique_ptr<COLUMN_DATA_S>, std::default_delete<COLUMN_DATA_S>> to void * exists.
The function in question is CPtrArray::Add. I am still trying to gras the smart pointers and their usage in a context like this.
Note that we should not be deleting the pointer when the function ends. The deletion of the collection is done in a dialog event handler.
When I use any of the codes in this page without modifying anything: https://wiki.gnome.org/Projects/Vala/AsyncSamples
I always get:
warning: ‘g_simple_async_result_new’ is deprecated: Use 'g_task_new' instead.
So I proceed with the recommendation of using GTask. However, when I try to use GLib.Task in Vala, I get stuck just declaring a task. So instead of using async from GIO in my own code, since it is deprecated, I try to use GLib.Task to simply update the label of a Gtk Button with numbers from a for loop, such that the code looks like this:
using Gtk;
Button button;
public static int main (string[] args) {
Gtk.init (ref args);
var window = new Window ();
window.title = "Count without blocking the UI";
window.border_width = 10;
window.window_position = WindowPosition.CENTER;
window.set_default_size (350, 70);
window.destroy.connect (Gtk.main_quit);
button = new Button.with_label ("Start counting");
button.clicked.connect (() => {
GLib.Task task = new GLib.Task(button, new Cancellable());
});
window.add (button);
window.show_all ();
Gtk.main ();
return 0;
}
void count(){
for(int i = 0; i < 10000; i++){
button.label = i.to_string();
}
}
But when compiling I get: error: ‘_data_’ undeclared (first use in this function)
_tmp3_ = g_task_new_finish (_data_->_res_);
The line number 15 is what is causing the compiler to throw that error. It comes from the C code that the vala compiler generates.
The main problem I found is that the GTask constructor signatures in Vala are different from C. Therefore, I could not re-create the code found here: GUI becomes unresponsive after clicking the button using GTK+ in C
Because for starters, I am not allowed to pass more than two arguments to the GLib.Task object constructor. The constructors of the Task object are different in each language. The constructor for GLib.Task in Vala can be found here.
Hence my question:
Are there any examples on how to use GLib Task (GTask) in Vala to perform an operation that updates the UI without blocking it? If not, is there another way to update the UI without blocking it? A way that is not deprecated?
Thanks.
P.S: I have already tried GLib.Thread, GLib.ThreadPool, and GLib.Idle. They all block the UI while in the for loop. GLib.Idle does not block the UI completely, but it renders it buggy in the sense that it becomes really slow to respond to user input while the loop is running.
It's perfectly fine to use async and there's some work already for porting the current code to use GTask.
Your counting code is blocking, so even if its execution is cushioned with GTask, it will still block the UI.
The correct way of performing CPU intensive background operations either use subprocess asynchronously or launch the work in a thread and dispatch in the main loop.
async void test_async () {
new Thread<void> (() => {
// count here...
test_async.callback ();
});
yield;
}
The GTask or more generally GAsyncResult only provide a container for holding the result of an asynchronous operation. They also recommend to use a GThreadPool, but it's a bit more boilerplate.
Another interesting thing is that test_async.callback is actually a SourceFunc, so you can pass it around in GLib.Timeout.
EDIT:
To fit more your question, if you want to update the UI while it progress, use an async loop:
async test_callback () {
for (var i = 0; i < 10000; i++) {
button.label = i.to_string ();
Idle.add (test_async.callback);
yield; // pause execution until retriggered in idle
}
}
Here's a full and working example:
using Gtk;
Button button;
public static int main (string[] args) {
Gtk.init (ref args);
var window = new Window ();
window.title = "Count without blocking the UI";
window.border_width = 10;
window.window_position = WindowPosition.CENTER;
window.set_default_size (350, 70);
window.destroy.connect (Gtk.main_quit);
button = new Button.with_label ("Start counting");
button.clicked.connect (() => {
count ();
});
window.add (button);
window.show_all ();
Gtk.main ();
return 0;
}
async void count(){
for(int i = 0; i < 10000; i++){
button.label = i.to_string();
Idle.add (count.callback);
yield;
}
}
i have a class in a cs file:
public class ThreadData
{
private int index;
public ThreadData(int index)
{
this.index = index;
}
public static ThreadDataOutput DoWork(ThreadDataInput input)
{
return new ThreadDataOutput();
}
}
now, i have c++ code that tries to init a new task and to us the above function:
int numOfThread = 2;
array<Task^>^ taskArr = gcnew array<Task^>(numOfThread);
for (int i = 0; i < numOfThread; i++)
{
ThreadData^ td = gcnew ThreadData(i);
ThreadDataInput^ input = gcnew ThreadDataInput(i);
Task<ThreadDataOutput^>^ task = gcnew Task<ThreadDataOutput^>(td->DoWork, input);
taskArr[i] = task;
taskArr[i]->Start();
}
Task::WaitAll(taskArr, 300 * 1000);
the following code return 2 errors at compile time:
can't take address of 'ThreadData::DoWork' unless creating delegate instance
cannot convert argument 1 from 'AmadeusWS::ThreadDataOutput ^(__clrcall *)(AmadeusWS::ThreadDataInput ^)' to 'System::Func ^
i also tried to declare a delegate like this in the cs file:
public static Func<ThreadDataInput, ThreadDataOutput> DoWork2 = delegate(ThreadDataInput taskDataInput)
{
return new ThreadDataOutput();
};
but i don't know how to call it from the c++\cli code
can anyone assist me to understand how to define cli delegate that can take parametr ?
thanks
In order to create a delegate instance in C++/CLI, you need to construct it explicitly, and specify the object that it will be called on separately from the class & method to be called.
gcnew Func<TInput, TOutput>(theObject, &TheClass::MethodToInvoke)
Note that the method to be called is specified in the C++ style.
Substituting that in to your task creation, I believe this statement will work for you:
Task<ThreadDataOutput^>^ task = gcnew Task<ThreadDataOutput^>(
gcnew Func<ThreadDataInput^, ThreadDataOutput^>(td, &ThreadData::DoWork),
input);
Edit
In the code you posted in your comment, you missed the object to invoke the delegate on.
gcnew Func<Object^, Object^>(td, &ThreadData::DoWork)
^^
When using ATL, what is the best way to display the progress of a background thread (e.g. when it's searching for a file) without blocking the UI?
I still want to be able to process messages, to allow for a Cancel button and to possibly allow the user to continue working with the program while the search happens.
There is no ATL specific here. One of the ways to do is to update progress details into member variable and post a message to GUI window, then handle the message by pulling the data from member variable and updating GUI, such as updating static and/or progress bar.
Worker thread pseudo-code:
m_DataCriticalSection.Lock();
m_nProgress = (INT) (nCurrent * 100 / nTotal);
m_DataCriticalSection.Unlock();
PostMessage(WM_MYUPDATEPROGRESS);
Window:
OnMyUpdateProgress()
{
m_DataCriticalSection.Lock();
INT nProgress = m_nProgress;
m_DataCriticalSection.Unlock();
m_ProgressBar.SetPos(nProgress);
}
UPD. A real code snippet, AddText is called on background thread, :
VOID AddText(const CString& sText)
{
_A(sText.Find(_T('\n')) < 0);
BOOL bIsTextEmpty;
{
CRoCriticalSectionLock TextLock(m_TextCriticalSection);
bIsTextEmpty = m_sText.IsEmpty();
m_sText.Append(sText);
m_sText.Append(_T("\r\n"));
}
if(bIsTextEmpty)
PostPrivateMessage(WM_UPDATETEXT);
}
And the code handler:
BEGIN_MSG_MAP_EX(CMainDialog)
// ...
MESSAGE_HANDLER_EX(WM_UPDATETEXT, OnUpdateText)
LRESULT OnUpdateText(UINT, WPARAM, LPARAM)
{
CString sText;
{
CRoCriticalSectionLock TextLock(m_TextCriticalSection);
sText = m_sText;
m_sText.Empty();
}
if(!sText.IsEmpty())
{
m_TextEdit.SetValue(m_TextEdit.GetValue() + sText);
const INT nTextLength = m_TextEdit.GetWindowTextLength();
m_TextEdit.SetSel(nTextLength, nTextLength);
}
return 0;
}
This uses custom classes (not 'pure' ATL), but I hope you get the idea.
Please help me. I am struck-up with thread concept. Actually my problem : I want to display the cities List in the combobox. I am getting cities list from the webservice. I am using thread for update the combo box value after webserice call finished.
Here I can call the webservice. But I couldn't get the Reply.
I am using the following code.
MainWindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
CGNetwork *cgNetwork = new CGNetwork();
ui->setupUi(this);
renderThread = new RenderThread(cgNetwork);
renderThread->start();
connect(renderThread,SIGNAL(finished()),this,SLOT(initControls()));
}
void MainWindow::initControls()
{
CGMainWindowUtility *pointer = CGMainWindowUtility::instance();
QStringList cityitems;
cityitems <<tr("All");
cityitems.append(pointer->getCityList());
QStringListModel *cityModel = new QStringListModel(cityitems, this);
ui->cityComboBox->setModel(cityModel);
}
RenderThread.cpp:
RenderThread::RenderThread(CGNetwork *cgnetwork)
{
cityUrl = "http://112.138.3.181/City/Cities";
categoryUrl = "http://112.138.3.181/City/Categories";
}
void RenderThread::run()
{
qDebug()<< "THREAD Started";
CGNetwork *cgnetworks = new CGNetwork();
cgnetworks->getCityList(cityUrl);
}
CGNetwork.cpp:
void CGNetwork ::getCityList(const QUrl url)
{
cityGuideNetworkAccessManager = new QNetworkAccessManager(this);
qDebug()<<"connection";
connect(cityGuideNetworkAccessManager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(parseCityList()));
const QNetworkRequest cityRequest(url);
cityReply= cityGuideNetworkAccessManager->get(cityRequest);
connect(cityReply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError()));
}
void CGNetwork::parseCityList()
{
qDebug()<<"Parsing";
cgParser = new CGJsonParser();
cgParser->CityRead(cityReply);
}
Since QNetworkAccessManager works asynchronously, there's no need for a separate thread. You can call getCityList directly from your main thread and it won't block.
I think your slots weren't called because your QThread::run returned before any of the work its been doing had a chance to complete, since getCityList just initiated an http request without waiting for it (because QNetworkAccessManager::get doesn't block like I said above).
Also as a side note, your slots aren't getting the same parameters as their corresponding signals, I don't remember if Qt supports this.