I'm having trouble finding out how to run a method in a seperate thread in C++ (using Visual C++ 2008), I've tried a number of ways to do this but none of them so far have been successful.
I'm very new to C++ but a fairly experienced programmer in Java, but have been given a task to fix some bugs in an old C++ application. The program uses an object called 'Mpeg' to control packetising and depackitising an Mpeg file. After setting up an Mpeg object properly, mpeg.Depacketise needs to be called, which then runs the method DepacketiseInputFile().
I've tried to make DepacketiseInputFile() run in a seperate thread by both using _beginthread and the System::Threading::Thread object using
Thread^ th = gcnew Thread(gcnew ThreadStart(DepacketiseInputFile));
however this returns the errors
using &Mpeg::Depacketise gives the error
when using _beginthread the code I tried was
However with this I constantly had trouble getting the arguments correct, with errors like
cropping up.
Is there any simple way to do this that anyone can reccomend? I've spent a few days playing around with this but seem to be getting nowhere :(
Any help would be greatly appreciated.
Cheers.
What kind of type is Mpeg? What kind of method is DepacketiseInputFile?
If it's a regular unmanaged, C++ class, then use _beginthread, but you have to make DepacketiseInputFile a static. It cannot take a member function.
Also, don't call DepacketiseInputFile with DepacketiseInputFile(), pass it in with
&Mpeg::DepacketiseInputFile
You should use the void* you get to pass it to pass in a pointer to the Mpeg object (and then cast it back).
If you want to use ThreadStart, then Mpeg needs to be a managed class.
EDIT: If you want to make DepacketiseInputFile, but it needs to access the object, then you use the void* argument to pass in a pointer.
So in the .h:
void DepacketiseInputFileMember();
static void DepacketiseInputFile(void *thisObj);
Your code goes in DepacketiseInputFileMember(), and write DepacketiseInputFile like this:
void Mpeg::DepacketiseInputFile(void *thisObj)
{
Mpeg* mpeg = reinterpret_cast<Mpeg*>(thisObj);
mpeg->DepacketiseInputFileMember();
}
When you call _beginthread, use this
_beginnthread(&Mpeg::DepacketiseInputFile, (unsigned)0, anMpegObjectPointer);
where anMpegObjectPointer is a pointer to an object of type Mpeg. You have to make sure the lifetime of the object is longer than it would be needed in the thread.
Forgive my syntax, I am writing this in a textarea, not Visual Studio
Change
_beginthread(DepacketiseInputFile(), (unsigned)0, (void *)NULL);
to
_beginthread(DepacketiseInputFile, (unsigned)0, (void *)NULL);
You wanna pass the address of the function to run (DepacketiseInputFile) and not its return value of that function (which is what you get from DepacketiseInputFile()).
I'm assuming DepacketiseInputFile is declared as void DepacketiseInputFile(void*), and is not a non-static member function of some class. Otherwise, the types won't match even when you do remove the brackets.
Related
I'm trying to import a COM interface into VC++. The COM object is from a application called IDEA, but as that is not very easy to get a hold of for others to help me. So I figure that if someone could give me instructions as to how I would do this for Word, it would be equivalent.
IDEA does have a .tlb file, but it would appear that it is incomplete. I can access the COM API using python with an example being something like this:
if __name__ == "__main__":
dbName = "Sample-Employees.IMD"
idea = win32ComClient.Dispatch(dispatch="Idea.IdeaClient")
db = idea.OpenDatabase(dbName) # open db
table_def = db.TableDef() # get table definition
Using the .tbl file, I can get as far as this:
#import "D:\Program Files (x86)\CaseWare IDEA\IDEA\Idea.tlb"
#include "x64\Debug\idea.tlh"
#include "x64\Debug\idea.tli"
void fn()
{
Idea::IIdeaClientPtr client;
auto db = client->OpenDatabase("Sample-Employees.IMD");
db-> // interface not defined
}
Intellisense will complete after the db-> with the following: AddRef, GetIdOfNames, GetTypeInfo, GetTypeInfoCount, Invoke, QueryInterface and Release. Thus, what I mean by an incomplete interface definition.
Now, since the python example states Idea.IdeaClient, and I've seen this with word as well (i.e. word.application), I was thinking that it might be possible to use that. Looking around though, I can't seem to find reference to that using #import. I have seen it being used with CLSIDFromProgID, but that is very manual mechanism. COM SMARTPTRs would be far more preferable.
Is this even possible to do with VC++?
Maybe OpenDatabase returns IDispatch, but interface containing TableDef is still defined in TLB.
In this case you'll need to downcast IDispatch to I-something-containing-TableDef-method.
Use QueryInterface call to get derived interface from IDispatch, not C or C++ casts, such as static_cast.
Otherwise, you'll need to use IDispatch::Invoke. The best help you have is CComPtr<IDispatch> from ATL, this template specialization have Invoke helpers, so that you can do something like this:
CComPtr<IDispatch> p;
p = db;
CComVairant result;
p.Invoke("TableDef", &result);
Or use IDispatch::Invoke as is.
Python aways relies on IDispatch::Invoke and does not use static interfaces, that's why it does not encounter this problem.
I'm trying to write a DLL to access the C++-only method Windows.Devices.Bluetooth.BluetoothLEDevice.Close() from a C# app. I seem to be able to access the class, but Visual Studio will not build if I try to use that one method. It is shown in the member list that comes up as you type, and there is no Intellisense error, just the build error.
using namespace Windows::Devices::Bluetooth;
__declspec(dllexport) void CloseBleDeviceUnmanaged(BluetoothLEDevice^device)
{
if (device->ConnectionStatus == BluetoothConnectionStatus::Connected) //no complaints for a property
{
device->GetDeviceSelector(); //no complaints for a method either
device->Close(); //Error C2039 | 'Close': is not a member of 'Windows::Devices::Bluetooth::BluetoothLEDevice'
}
return;
}
How do I get this to at least build?
(edit: removed extraneous syntax problem as per Nico Zhu - MSFT's answer)
You don't need to do any of this to call Close from C# -- just put it in a using(...) block and it will dispose (close) the object for you automatically. If the lifetime of the objection doesn't lend itself well to a using block, you can simply call IDisposabe.Dispose() on it directly.
For the compiler error, this is documented in MSDN since you should never call Close from C++/CX:
Close methods aren't callable through Visual C++ component extensions (C++/CX) on Windows Runtime class instances where the class implemented IClosable. Instead, Visual C++ component extensions (C++/CX) code for runtime classes should call the destructor or set the last reference to null.
The parameter of CloseBleDeviceUnmanaged method is Value Type in your case. When you pass a parameter to the method. The parameter will generate a copy. And the original parameter has not been changed. Please pass the
reference type parameter like the follow.
void DevicesTool::CloseBleDeviceUnmanaged(Windows::Devices::Bluetooth::BluetoothLEDevice^device)
{
if (device->ConnectionStatus == BluetoothConnectionStatus::Connected)
{
device->GetDeviceSelector();
device->Close();
}
}
As for the Close method, I have reproduced the issue in my side(target platform version 16299), and I have report this to related team. Please pay attention to the thread update.
I have a code that's a mixture of standard C++ and cli/c++
And i am trying to create the following object:
std::map<std::string, System::IO::StreamWriter> streamWrite;
But it doesn't work. the compiler actually crushes when i try to build the project.
Is there a way to make it work?
Edit: my code is originally cli c++ and i am slowly converting it to native c++, and that's why i have a mixture of both native and managed objects.
I have no idea yet how to convert StreaReader and StreamWriter objects to native C++ so i am leaving this to the very end, so i n the meantime i have this "strange creature" - std::map that holds a managed object as its value.
Standard C++ things (like std::map) and managed objects do not mix out of the box. It is however possible by using GCHandle to get an intptr_t that represents a managed object and keeps it from getting garbage collected.
intptr_t GetHandle(System::Object^ obj) {
auto gch = System::Runtime::InteropServices::GCHandle::Alloc(obj);
auto ip = System::Runtime::InteropServices::GCHandle::ToIntPtr(gch);
return static_cast<intptr_t>(ip);
}
Now you have an intptr_t that represents a your object and can be freely used with native C++ code. std::map<std::string, intptr_t>
To get your object from the intptr_t:
System::IO::StreamWriter^ ToStreamWriter(intptr_t h) {
auto gch = System::Runtime::InteropServices::GCHandle::FromIntPtr(static_cast<System::IntPtr>(h));
return safe_cast<System::IO::StreamWriter^>(gch.Target);
}
The intptr_t value represents a resource the needs to be freed so it should ideally be kept in an RAII object whose destructor will convert it back to a GCHandle (see above) and Free() that. If you don't do that then the StreamWriter object will never be garbage collected and you have a heap leak.
void FreeHandle(intptr_t h) {
auto gch = System::Runtime::InteropServices::GCHandle::FromIntPtr(static_cast<System::IntPtr>(h));
gch.Free();
}
I made a whole template library to do this kind of stuff so I can use managed objects freely in native code. I like this kind of thing but I don't necessarily recommend it. I've never seen it done by anyone else, but it does work nicely once you build good tools for it.
this is the source of the issue. My answer there was deleted with the hint to start a new question. So, here we go:
I want to pass the managed reference of this to unmanaged code. And then call the managed callback out of the unmanaged callback.
public ref class CReader
with a private field
private:
[...]
void *m_pTag;
[...]
In the constructor of the managed class I initialize the m_pTag like this:
m_pTag = new gcroot<CReader ^>(this);
Later, I pass this void *m_pTag to the unmanaged code. If the unmanaged callback is called, I'm casting the void *m_pTag back to managed reference and call the managed callback
(*(gcroot<CReader ^>*)pTag)->MANAGEDCALLBACKFUNCTION
and there is an exception thrown, if the DLL is used under another AppDomain. The Debugger stops in gcroot.h on line
// don't return T& here because & to gc pointer not yet implemented
// (T should be a pointer anyway).
T operator->() const {
// gcroot is typesafe, so use static_cast
return static_cast<T>(__VOIDPTR_TO_GCHANDLE(_handle).Target);
}
with Cannot pass a GCHandle across AppDomains.
My question is, what should I do?
Sincerly,
Sebastian
==================== EDIT ====================
I am now able to reproduce the problem. I've took some screenshots, to show the issue.
1st screenshot: constructor
snd screenshot: callback
The problem is, that the value-member of the struct gcroot in the callback is empty.
Thank you.
Sebastian
==================== EDIT ====================
Push.
The gcroot<> C++ class is a wrapper that uses the GCHandle class. The constructor calls GCHandle.ToIntPtr() to turn the handle into an opaque pointer, one that you can safely store as a member of an unmanaged struct or C++ class.
The cast then, later, converts that raw pointer back to the handle with the GCHandle.FromIntPtr() method. The GCHandle.Target property gives you the managed object reference back.
GCHandle.FromIntPtr() can indeed fail, the generic exception message is "Cannot pass a GCHandle across AppDomains". This message only fingers the common reason that this fails, it assumes that GCHandle is used in safe code and simply used incorrectly.
The message does not cover the most common reason that this fails in unsafe code. Code that invariably dies with undiagnosable exceptions due to heap corruption. In other words, the gcroot<> member of the C++ class or struct getting overwritten with an arbitrary value. Which of course dooms GCHandle.FromIntPtr(), the CLR can no longer find the handle back from a junk pointer value.
You diagnose this bug the way you diagnose any heap corruption problem. You first make sure that you get a good repro so you can trip the exception reliably. And you set a data breakpoint on the gcroot member. The debugger automatically breaks when the member is written inappropriately, the call stack gives you good idea why this happened.
How do i define std::string as a type so i can emit from text from a QThread to another using a queued connection?
I searched and found many threads solving this issue.
Here one of the other threads available
Here other thread
But all of them consider the connection as a Qt 4 version. (Signal and slot macros).
Now, after 3 years, with Qt5.2 available, i'm not able to find a solution, even when the documentation says:
Docs about Q_DECLARE_METATYPE
I have to define the type in my header.
I tried where Q_OBJECT goes, and outside the class. Apparently outside works.
I tried:
Q_DECLARE_METATYPE<std::string>();
Q_DECLARE_METATYPE(std::string);
Q_DECLARE_METATYPE<std::string>;
Q_DECLARE_METATYPE<std::string>(anothername declared for std::string with typedef);
All of them gave me errors, from: Syntax error : missing ';' before , to unnable to missing type specifier, int assumed.
The problem is always with this line of code.
and i´m using this in the same class within a method, which registers the type just before connecting the new thread with a queued connection:
qRegisterMetaType<std::string>();
QObject::connect(worker, &Raytracer::textEmitted, gui_, &MainWindow::addText, Qt::QueuedConnection);
What am i doing wrong?
I got to make it work by using QString. But how could i make it work with std::string?
Q_DECLARE_METATYPE should be placed outside any class or function.
The docs state: Ideally, this macro should be placed below the declaration of the class or struct. If that is not possible, it can be put in a private header file which has to be included every time that type is used in a QVariant.
The correct syntax is Q_DECLARE_METATYPE(std::string) without the ;.
But this is all useless if you are not using templates. As the docs state: Declare new types with Q_DECLARE_METATYPE() to make them available to QVariant and other template-based functions. Call qRegisterMetaType() to make type available to non-template based functions, such as the queued signal and slot connections.
So all you need to do is call qRegisterMetaType, and you will be able to use std::string in your queued connections. Do something like this somewhere before your connect statement:
qRegisterMetaType<std::string>("std::string");
Include the qmetatype.h header in the file which uses Q_DECLARE_METATYPE.