porting _beginthreadex from VS2005 to VS2017
-
I'm attempting to port some old code from VS2005 C++ to VS2017. One problem that I don't know how to address is how to convert the old _beginthreadex to what would be the correct call in 2017. VS2005 code is calling a member function that is in a class: m_sendThread = (HANDLE)_beginthreadex(NULL, 0, sendData, this, 0, &this->m_sendThreadId); I've casted it it to: m_sendThread = (HANDLE)_beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))sendData, this, 0, &this->m_sendThreadId); Code compiles but will exit with code 1073741855. The class is a class that handles winsock connections and sendData is called to start the thread once the socket has been established. I am new to classes and even newer to threaded programs so it is hard for me to say how to fix this problem. Any help would be much appreciated. Thanks.
-
I'm attempting to port some old code from VS2005 C++ to VS2017. One problem that I don't know how to address is how to convert the old _beginthreadex to what would be the correct call in 2017. VS2005 code is calling a member function that is in a class: m_sendThread = (HANDLE)_beginthreadex(NULL, 0, sendData, this, 0, &this->m_sendThreadId); I've casted it it to: m_sendThread = (HANDLE)_beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))sendData, this, 0, &this->m_sendThreadId); Code compiles but will exit with code 1073741855. The class is a class that handles winsock connections and sendData is called to start the thread once the socket has been established. I am new to classes and even newer to threaded programs so it is hard for me to say how to fix this problem. Any help would be much appreciated. Thanks.
The
_beginthreadex()
version used by your code has not changed since VC 6.0 besides the return type: VS6: _beginthread, _beginthreadex[^]:unsigned long _beginthreadex( void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr );
_beginthread, _beginthreadex | Microsoft Docs[^]:
uintptr_t _beginthreadex( // NATIVE CODE
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);If you need to cast the
start_address
parameter (yoursendData
function) to avoid compilation errors, you should change the function declaration instead. Never use casting for such (address of function) parameters but provide correct types. An exit code of 1073741855 (0X4000001F) is not a standard error code. You might use the debugger to find out when and under which conditions your application exits with that code. I guess that the problem is not related to_beginthreadex()
but to the thread function itself or even any other parts of your application. -
I'm attempting to port some old code from VS2005 C++ to VS2017. One problem that I don't know how to address is how to convert the old _beginthreadex to what would be the correct call in 2017. VS2005 code is calling a member function that is in a class: m_sendThread = (HANDLE)_beginthreadex(NULL, 0, sendData, this, 0, &this->m_sendThreadId); I've casted it it to: m_sendThread = (HANDLE)_beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))sendData, this, 0, &this->m_sendThreadId); Code compiles but will exit with code 1073741855. The class is a class that handles winsock connections and sendData is called to start the thread once the socket has been established. I am new to classes and even newer to threaded programs so it is hard for me to say how to fix this problem. Any help would be much appreciated. Thanks.
You are calling a class member function you must shim it thru a static function
#include
class foo
{
public:
void startTheThread()
{
// Start the thread for the sendData (we send "this" in as a parameter for shim)
// The shim code is a static block for all your class instances hence &foo::sendData
// It creates a class member call from the value of "this" passed in
sendHandle = _beginthreadex(0, 0, &foo::sendData, this, 0, &this->m_sendThreadId);
}
private:
void sendDataMemberCall()
{
/* All the real send code goes here */
}static unsigned \_\_stdcall sendData(void \*p\_this) { /\* This is just a shim to convert a thread call to a member function call \*/ foo\* p\_foo = static\_cast(p\_this); p\_foo->sendDataMemberCall(); // call Non-static member function! return 0; } uintptr\_t sendHandle; unsigned m\_sendThreadId;
};
As a quick explaination a non static class function has a hidden invisible pointer which you know as "this". So if you look at the member function sendDataMemberCall it looks like this to you
void sendDataMemberCall()
At a code level it actually looks like this
void sendDataMemberCall (foo* this);
The compiler pushes down a hidden pointer to the actual instance of the class object So if we had two instances foo1 and foo2 when you call sendDataMemberCall they actually do this
sendDataMemberCall (&foo1);
sendDataMemberCall (&foo2);So the problem is the format we need for the thread function doesn't match the class function and we can't make them match because of the hidden local instance push. However the thread function does allow us to pass a parameter and we pass "this" as a parameter. So now what you can do is typecast this back to a pointer to it's type and get the compiler to call the pointer member function and it will magically push our hidden pointer. So all the shim is really doing is turning function + extra parameter into push paramater call class function.
In vino veritas
-
The
_beginthreadex()
version used by your code has not changed since VC 6.0 besides the return type: VS6: _beginthread, _beginthreadex[^]:unsigned long _beginthreadex( void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr );
_beginthread, _beginthreadex | Microsoft Docs[^]:
uintptr_t _beginthreadex( // NATIVE CODE
void *security,
unsigned stack_size,
unsigned ( __stdcall *start_address )( void * ),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);If you need to cast the
start_address
parameter (yoursendData
function) to avoid compilation errors, you should change the function declaration instead. Never use casting for such (address of function) parameters but provide correct types. An exit code of 1073741855 (0X4000001F) is not a standard error code. You might use the debugger to find out when and under which conditions your application exits with that code. I guess that the problem is not related to_beginthreadex()
but to the thread function itself or even any other parts of your application. -
You are calling a class member function you must shim it thru a static function
#include
class foo
{
public:
void startTheThread()
{
// Start the thread for the sendData (we send "this" in as a parameter for shim)
// The shim code is a static block for all your class instances hence &foo::sendData
// It creates a class member call from the value of "this" passed in
sendHandle = _beginthreadex(0, 0, &foo::sendData, this, 0, &this->m_sendThreadId);
}
private:
void sendDataMemberCall()
{
/* All the real send code goes here */
}static unsigned \_\_stdcall sendData(void \*p\_this) { /\* This is just a shim to convert a thread call to a member function call \*/ foo\* p\_foo = static\_cast(p\_this); p\_foo->sendDataMemberCall(); // call Non-static member function! return 0; } uintptr\_t sendHandle; unsigned m\_sendThreadId;
};
As a quick explaination a non static class function has a hidden invisible pointer which you know as "this". So if you look at the member function sendDataMemberCall it looks like this to you
void sendDataMemberCall()
At a code level it actually looks like this
void sendDataMemberCall (foo* this);
The compiler pushes down a hidden pointer to the actual instance of the class object So if we had two instances foo1 and foo2 when you call sendDataMemberCall they actually do this
sendDataMemberCall (&foo1);
sendDataMemberCall (&foo2);So the problem is the format we need for the thread function doesn't match the class function and we can't make them match because of the hidden local instance push. However the thread function does allow us to pass a parameter and we pass "this" as a parameter. So now what you can do is typecast this back to a pointer to it's type and get the compiler to call the pointer member function and it will magically push our hidden pointer. So all the shim is really doing is turning function + extra parameter into push paramater call class function.
In vino veritas
-
You are calling a class member function you must shim it thru a static function
#include
class foo
{
public:
void startTheThread()
{
// Start the thread for the sendData (we send "this" in as a parameter for shim)
// The shim code is a static block for all your class instances hence &foo::sendData
// It creates a class member call from the value of "this" passed in
sendHandle = _beginthreadex(0, 0, &foo::sendData, this, 0, &this->m_sendThreadId);
}
private:
void sendDataMemberCall()
{
/* All the real send code goes here */
}static unsigned \_\_stdcall sendData(void \*p\_this) { /\* This is just a shim to convert a thread call to a member function call \*/ foo\* p\_foo = static\_cast(p\_this); p\_foo->sendDataMemberCall(); // call Non-static member function! return 0; } uintptr\_t sendHandle; unsigned m\_sendThreadId;
};
As a quick explaination a non static class function has a hidden invisible pointer which you know as "this". So if you look at the member function sendDataMemberCall it looks like this to you
void sendDataMemberCall()
At a code level it actually looks like this
void sendDataMemberCall (foo* this);
The compiler pushes down a hidden pointer to the actual instance of the class object So if we had two instances foo1 and foo2 when you call sendDataMemberCall they actually do this
sendDataMemberCall (&foo1);
sendDataMemberCall (&foo2);So the problem is the format we need for the thread function doesn't match the class function and we can't make them match because of the hidden local instance push. However the thread function does allow us to pass a parameter and we pass "this" as a parameter. So now what you can do is typecast this back to a pointer to it's type and get the compiler to call the pointer member function and it will magically push our hidden pointer. So all the shim is really doing is turning function + extra parameter into push paramater call class function.
In vino veritas
Thanks again for the detailed response. I attempted to apply this calling convention into my program and really got twisted up with outer errors. So I'm trying now to do this again. Still getting errors: error C2664: '_beginthreadex' : cannot convert parameter 3 from 'unsigned int (__clrcall *)(void *)' to 'unsigned int (__stdcall *)(void *)' Address of a function yields __clrcall calling convention :confused: Jim
-
Thanks again for the detailed response. I attempted to apply this calling convention into my program and really got twisted up with outer errors. So I'm trying now to do this again. Still getting errors: error C2664: '_beginthreadex' : cannot convert parameter 3 from 'unsigned int (__clrcall *)(void *)' to 'unsigned int (__stdcall *)(void *)' Address of a function yields __clrcall calling convention :confused: Jim
The compiler is trying to link to managed code ... see the 4 different definitions of beginthreadex _beginthread, _beginthreadex[^] You are probably compiling with the managed code probably because you have clr compilation turned on. pull down menu .... debug -> program name properties -> general tab -> No Common Language Runtime Support Turn it to "No Common Language Runtime Support" If you dont understand managed vs native code Difference between managed and unmanaged code[^] Now the funny part if you do need managed code you will have to shim the shim ... yes an irony but the pointer that is being passed has to be converted. If you can't work it out drop me a line and I will do it for you.
In vino veritas
-
The compiler is trying to link to managed code ... see the 4 different definitions of beginthreadex _beginthread, _beginthreadex[^] You are probably compiling with the managed code probably because you have clr compilation turned on. pull down menu .... debug -> program name properties -> general tab -> No Common Language Runtime Support Turn it to "No Common Language Runtime Support" If you dont understand managed vs native code Difference between managed and unmanaged code[^] Now the funny part if you do need managed code you will have to shim the shim ... yes an irony but the pointer that is being passed has to be converted. If you can't work it out drop me a line and I will do it for you.
In vino veritas
Thank again Leon. Seems like anything I change in the compiler just makes things 1000 times worse. I looks like there is going to be no easy way to get this to work and I may have to take a couple of classes to get up to speed. Thanks for all your time and thorough answers. Jim :confused:
-
Thank again Leon. Seems like anything I change in the compiler just makes things 1000 times worse. I looks like there is going to be no easy way to get this to work and I may have to take a couple of classes to get up to speed. Thanks for all your time and thorough answers. Jim :confused:
No it's not that hard it means your code contains managed code (that is all the pointers are garbage collected), so put the settings back. I can't tell because I have no idea what the code itself looks like or does you just gave us a small piece. So all the issue is that you have managed code and you need the pointer to be passed to beginthreadex to be a managed pointer and if you want me to create the shim I can.
In vino veritas
-
No it's not that hard it means your code contains managed code (that is all the pointers are garbage collected), so put the settings back. I can't tell because I have no idea what the code itself looks like or does you just gave us a small piece. So all the issue is that you have managed code and you need the pointer to be passed to beginthreadex to be a managed pointer and if you want me to create the shim I can.
In vino veritas
I really wish I didn't have to keep dragging this one, but I still don't know why this won't work. It worked perfectly fine in VS2005, but now I can't make a thread? Doesn't make sense. From what I have learned the original code was built with /CLR:pure or managed. I've tried to remove this but the error are so extensive I don't think I would ever get through them. Is there another function I can use to create the thread? Or some other way to get this thread to run without rewriting the whole thing? Any help would be much appreciated. This is where I'm at now: #include "Connection.h" #include "Packet.h" #include "Opcodes.h" using Ig::Connection; using Ig::CriticalSection; using namespace Ig; CriticalSection::CriticalSection() { ... Connection::Connection() { ... } Connection::~Connection() { // Wait for the thread to exit. exitThreads(); // Clear any pending opcodes. clearPendingOpcodes(); } bool Connection::open(const char* ip, const int port, const int timeOut) { // Make sure the threads are not already running. exitThreads(); // Setup the ports // Setup the sockets. // Set the receive socket timeout. // Set up the send address. // Setup the receive address // Change the state to connecting // Connect to the sender address. if (::connect(m_sendSocket, (sockaddr *)&m_sendAddress, sizeof(m_sendAddress)) == SOCKET_ERROR) { int error = WSAGetLastError(); closesocket(m_sendSocket); return false; } // Bind to the receiver address. if (::bind(m_receiveSocket, (sockaddr *)&m_receiveAddress, sizeof(m_receiveAddress)) == SOCKET_ERROR) { int error = WSAGetLastError(); closesocket(m_sendSocket); closesocket(m_receiveSocket); return false; } // Clear pending opcodes. clearPendingOpcodes(); // Start the threads // THIS IS WHERE ALL MY PROBLEMS BEGIN!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // m_sendThread = (HANDLE)_beginthreadex(NULL, 0, sendData, this, 0, &this->m_sendThreadId); m_receiveThread = (HANDLE)_beginthreadex(NULL, 0, receiveData, this, 0, &this->m_receiveThreadId); return true; } unsigned int Connection::sendData(void* param) { bool request_thread_exit = false; ConnectionState state = Connection::connecting; // The param should be an instance of the connection class. if (param == 0) return 0; // Cast it to a connection Connection* connection = static
-
I really wish I didn't have to keep dragging this one, but I still don't know why this won't work. It worked perfectly fine in VS2005, but now I can't make a thread? Doesn't make sense. From what I have learned the original code was built with /CLR:pure or managed. I've tried to remove this but the error are so extensive I don't think I would ever get through them. Is there another function I can use to create the thread? Or some other way to get this thread to run without rewriting the whole thing? Any help would be much appreciated. This is where I'm at now: #include "Connection.h" #include "Packet.h" #include "Opcodes.h" using Ig::Connection; using Ig::CriticalSection; using namespace Ig; CriticalSection::CriticalSection() { ... Connection::Connection() { ... } Connection::~Connection() { // Wait for the thread to exit. exitThreads(); // Clear any pending opcodes. clearPendingOpcodes(); } bool Connection::open(const char* ip, const int port, const int timeOut) { // Make sure the threads are not already running. exitThreads(); // Setup the ports // Setup the sockets. // Set the receive socket timeout. // Set up the send address. // Setup the receive address // Change the state to connecting // Connect to the sender address. if (::connect(m_sendSocket, (sockaddr *)&m_sendAddress, sizeof(m_sendAddress)) == SOCKET_ERROR) { int error = WSAGetLastError(); closesocket(m_sendSocket); return false; } // Bind to the receiver address. if (::bind(m_receiveSocket, (sockaddr *)&m_receiveAddress, sizeof(m_receiveAddress)) == SOCKET_ERROR) { int error = WSAGetLastError(); closesocket(m_sendSocket); closesocket(m_receiveSocket); return false; } // Clear pending opcodes. clearPendingOpcodes(); // Start the threads // THIS IS WHERE ALL MY PROBLEMS BEGIN!!!!!!!!!!!!!!!!!!!!!!!!!!!! // // m_sendThread = (HANDLE)_beginthreadex(NULL, 0, sendData, this, 0, &this->m_sendThreadId); m_receiveThread = (HANDLE)_beginthreadex(NULL, 0, receiveData, this, 0, &this->m_receiveThreadId); return true; } unsigned int Connection::sendData(void* param) { bool request_thread_exit = false; ConnectionState state = Connection::connecting; // The param should be an instance of the connection class. if (param == 0) return 0; // Cast it to a connection Connection* connection = static
Patched code. Exchange/MSNRouteEditor_VS2017.zip at master · LdB-ECM/Exchange · GitHub[^]
In vino veritas
-
Patched code. Exchange/MSNRouteEditor_VS2017.zip at master · LdB-ECM/Exchange · GitHub[^]
In vino veritas