Create Thread in ObjectARX
Dear all,
I am trying to create a thread in an ObjectARX project. The function I used is
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, NULL, 0, &dwThreadId).
After the creation, the thread ID and Handle are returned successfully. But the created thread has never run.
Can anyone tell me if there is any way to solve this problem?
Cheers,
Yang
0 Likes
6 REPLIES
MESSAGE 2 OF 7
in reply to: zhengy
07-26-2006 02:10 PM
Yang:
> After the creation, the thread ID and Handle are returned successfully.
> But the created thread has never run.
How are you determining that the thread hasn't executed? Are you trying
to create the thread from within your DllMain() function?
--
Owen Wengerd
President, ManuSoft ==> http://www.manusoft.com
VP Americas, CADLock, Inc. ==> http://www.cadlock.com
0 Likes
MESSAGE 3 OF 7
in reply to: zhengy
07-27-2006 03:05 AM
Hi Owen,
Thanks for your kindly reply. I attached the snippet of my
code. I have registered a command "watch" in the command stack to activate the watchDb() method.
After the CreateThread () method, I got a returned value for the thread ID and the handle.
However, nothing is printed out like the first operation (print the sentence ("Already in Thread")), which makes me believe that the thread is not run.
I also use a software (like Windows Task Manager) that is able to trace all the Processes and their Threads, but the ThreadFun thread is not in the list.
void watchDb ()
{
if (gpDbr == NULL) {
gpDbr = new AsdkDbReactor();
}
acdbHostApplicationServices()->workingDatabase()->addReactor(gpDbr);
DWORD dwThreadId=0;
if (CreateThread(NULL,
0, (LPTHREAD_START_ROUTINE)ThreadFunc, NULL,
0, &dwThreadId) == NULL)
{
acutPrintf("NULL Thread\n");
return ;
}
acutPrintf("\nThread id = %d\n", dwThreadId);
}
void ThreadFunc () {
acutPrintf("Already in Thread\n");
...
}
Thanks a lot.
Yang
0 Likes
MESSAGE 4 OF 7
in reply to: zhengy
07-27-2006 03:57 AM
From objectARX doc:
"ObjectARX Does Not Support Multi-Threaded Programming
If you spawn multiple threads in your application, make sure that no more than one of the threads at a time invokes anything in the ObjectARX system."
MESSAGE 5 OF 7
in reply to: zhengy
07-27-2006 06:33 AM
From the MSDN docs for CreateThread(): "A thread in an executable that calls
the C run-time library (CRT) should use the _beginthread and _endthread
functions for thread management...".
Dan
wrote in message news:5250034@discussion.autodesk.com...
Hi Owen,
Thanks for your kindly reply. I attached the snippet of my
code. I have registered a command "watch" in the command stack to activate
the watchDb() method.
After the CreateThread () method, I got a returned value for the thread ID
and the handle.
However, nothing is printed out like the first operation (print the sentence
("Already in Thread")), which makes me believe that the thread is not run.
I also use a software (like Windows Task Manager) that is able to trace all
the Processes and their Threads, but the ThreadFun thread is not in the
list.
void watchDb ()
{
if (gpDbr == NULL) {
gpDbr = new AsdkDbReactor();
}
acdbHostApplicationServices()->workingDatabase()->addReactor(gpDbr);
DWORD dwThreadId=0;
if (CreateThread(NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadFunc, NULL,
0, &dwThreadId) == NULL)
{
acutPrintf("NULL Thread\n");
return ;
}
acutPrintf("\nThread id = %d\n", dwThreadId);
}
void ThreadFunc () {
acutPrintf("Already in Thread\n");
...
}
Thanks a lot.
Yang
0 Likes
MESSAGE 6 OF 7
in reply to: zhengy
07-27-2006 07:39 AM
Your acutPrintf() call is not a reliable way to check whether the thread
is executing, because ObjectARX API functions do not support asynchronous
reentrancy. Use something independent of AutoCAD (such OutputDebugString
under a debugger) to test whether your thread is executing.
--
Owen Wengerd
President, ManuSoft ==> http://www.manusoft.com
VP Americas, CADLock, Inc. ==> http://www.cadlock.com
0 Likes
MESSAGE 7 OF 7
in reply to: zhengy
07-27-2006 11:12 PM
Yes, Owen, you are definitely right. I tried other approaches and found out that the created thread was running, but it could not print out anything on the AutoCAD screen using acutPrintf().
Thanks for all the replies to help me out.
Cheers,
Yang Message was edited by: zhengy
0 Likes
How to use threads in ObjectARX?
By Adam Nagy
How do I call an AutoCAD command from a background thread? When I try to call AcApDocManager::sendStringToExecute(), it causes AutoCAD to terminate unexpectedly.
Solution
The AutoCAD API functions are not thread safe - i.e. they do not expect to be called from a thread other than the main one.
You can create your own threads for background processing, but if you need to call an ARX function (even just to execute an AutoCAD command), then you need to marshal that call to the main thread.
We have a DevNote on this topic concerning .NET AddIns called "Use Thread for background processing", and you can handle this situation in ARX in a similar fashion.
You could write a helper class that creates a message only window, that could be used to invoke AutoCAD functions on the main thread.
class MyInvoker
{
public:
MyInvoker()
{
WNDCLASS wndclass = {0};
wndclass.hInstance = _hdllInstance ;
wndclass.lpfnWndProc = wndProcedure;
wndclass.lpszClassName = L"MessageOnlyWindow";
// Register the class
ATOM a = RegisterClass(&wndclass);
// Create the window object
_hwnd = CreateWindow(L"MessageOnlyWindow",
NULL,
NULL,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
_hdllInstance,
NULL);
}
~MyInvoker()
{
DestroyWindow(_hwnd);
}
typedef void (*CallbackFunctionType)();
void InvokeSync(CallbackFunctionType funcPtr)
{
SendMessage(_hwnd, _wm, 0, (LPARAM)funcPtr);
}
private:
HWND _hwnd;
static DWORD _wm;
static LRESULT CALLBACK wndProcedure(
HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
if (Msg == _wm)
{
CallbackFunctionType funcPtr = (CallbackFunctionType)lParam;
(*funcPtr)();
}
return DefWindowProc(hWnd, Msg, wParam, lParam);
}
};
DWORD MyInvoker::_wm = RegisterWindowMessage(L"MyInvokeMessage");
// a global instance of the helper class
MyInvoker * g_myInvoker;
Create an instance of this class in the kInitAppMsg
virtual AcRx::AppRetCode On_kInitAppMsg (void *pkt)
{
// TODO: Load dependencies here
// You *must* call On_kInitAppMsg here
AcRx::AppRetCode retCode =AcRxArxApp::On_kInitAppMsg(pkt);
// TODO: Add your initialization code here
g_myInvoker = new MyInvoker();
return (retCode) ;
}
And delete it in the kUnloadAppMsg
virtual AcRx::AppRetCode On_kUnloadAppMsg (void *pkt)
{
// TODO: Add your code here
// You *must* call On_kUnloadAppMsg here
AcRx::AppRetCode retCode =AcRxArxApp::On_kUnloadAppMsg(pkt);
// TODO: Unload dependencies here
delete g_myInvoker;
return (retCode) ;
}
You would need to place the ARX calls into a seperate function that then could be invoked on the main thread. Note that now we are in session/application context so if you wanted to modify the database you would need to lock the document
void acadFunctionCalls()
{
AcApDocument * activeDoc = acDocManager->mdiActiveDocument();
acDocManager->sendStringToExecute(
activeDoc,
L"\x03\x03(command \"LINE\" \"0,0\" \"100,100\" \"\") ");
}
Here is the function that the background thread will execute
UINT thread( LPVOID pParam )
{
// we are doing some calculations in the thread
Sleep(3000);
// now we want to call some AutoCAD functions
g_myInvoker->InvokeSync(acadFunctionCalls);
return (0); //exit from thread
}
And here is the AutoCAD command that will start the background thread
static void MfcThreadTest_StartThread(void)
{
AfxBeginThread(thread,NULL,THREAD_PRIORITY_LOWEST);
}
Posted at 07:11 AM in Adam Nagy, AutoCAD, ObjectARX | Permalink