.NET Forum / .NET Framework / Interop / July 2006
C# Client and Unmanaged C++ DLL Threading
|
|
Thread rating:  |
Paul - 05 Jun 2006 16:45 GMT Hi, I wonder if you can help.... I have a C# client that calls into an unmanaged C++ DLL. The call never returns due to what appears to be a multi-threading issue.....
To complete the API request, the unmanaged DLL requires to create a second thread (windows message loop). Whislt the 2nd thread is busy, the main thread is blocked (and therefore the C# client). Unfortunately the 2nd thread does not run. (Calling the unmanaged DLL from a C++ console app works as desired).
I noticed that the main C# thread is a STA; I tried creating a 2nd thread in the C# client, setting the AttributeState in the 2nd client to MTA, and calling the C++ unmanaged DLL from the 2nd C# thread... with the same blocking failue.
Any ideas on calling a multi-threaded unmanaged DLL from a C# app?
cheers,
Paul
"Peter Huang" [MSFT] - 06 Jun 2006 04:34 GMT Hi Paul,
Thanks for your posting!
Based on my understanding, you have an unmanaged DLL, which exports a function. Is the DLL a COM DLL or just a legacy DLL?
Also if you create another thread in C# to do the API call, so only the second thread will be blocked due to the P/Invoke call is blocked. Is that correct? But if you call the DLL function in C++ all is OK.
From the description, to complete the API call, we need to create another thread. So how C++ did that? Can you describe the detailed steps about how the API call works one by one? so that we will know what the concrete scenario.
If I misunderstood, please feel free to post here.
Best regards,
Peter Huang
Microsoft Online Community Support ================================================== When responding to posts, please "Reply to Group" via your newsreader so that others may learn and benefit from your issue. ================================================== This posting is provided "AS IS" with no warranties, and confers no rights.
Paul - 06 Jun 2006 09:53 GMT Hi Peter, Thanks for your interest. Your interpretation is correct. I will add more details for clarification:
The C++ unmanaged DLL is a legacy DLL, not a COM DLL. Yes, the 2nd C# thread is blocked due to PInvoke blocking in the unmanaged C++ DLL.
/* C++ unmanaged DLL */ The C++ unmanaged DLL exports the following function: UINT __declspec(dllexport) GetServiceNames(char * aszServiceNames[], UINT * puiNoOfServiceNames);
When called, the C++ unmanaged DLL creates a 2nd thread in which some initialisation is performed. The thread is created using the Win32 API: CreateThread();
Whilst initialisation is being performed, the main thread is blocked using the Win32 API: WaitForSingleObject();
Once initialisation is complete, the main thread is released when the 2nd thread calls the Win32 API: SetEvent();
The C++ unmanaged DLL functions as required when called from a C++ console app. When called from the C# application, the call is blocked due to the main thread being blocked at WaitForSingleObject(), due to the 2nd thread failing to execute.
Modification for debug of the C++ unmanaged DLL to omit the WaitForSingleObject() call, prevents the API GetServiceNames() call from blocking and returns sucesfully marshaled test data.
Any thoughts on how to get the 2nd thread to run when called from the C# client?
thanks again,
Paul
> Hi Paul, > [quoted text clipped - 26 lines] > ================================================== > This posting is provided "AS IS" with no warranties, and confers no rights. "Peter Huang" [MSFT] - 07 Jun 2006 03:54 GMT Hi Paul,
Based on my test with a simple reproduce sample following your instruction, I can not reproduce the problem You may have a test and change the code below to reproduce the problem.
Also you may try to debug into your code to check which steps cause the problem. e.g. If CreateThread called succeed with GetLastError(); And if the code in the 2nd thread is running and if the SetEvent is called, did that succeed?
[C++] VOID ThreadFunction(LPVOID lpParam) { HANDLE hEvent = *(HANDLE*)lpParam; for(int i = 0;i<10;i++) { cout<<i<<": Running"<<endl; Sleep(1000); } if(SetEvent(hEvent)) cout<<"Event Signal succeeded"<<endl; else cout<<"Event signal failed"<<endl; }
CPPDLL_API int fnCPPDLL(void) { DWORD IDThread; HANDLE hEvent; HANDLE hThread; hEvent = CreateEvent( NULL, // default security attributes TRUE, // manual-reset event FALSE, // initial state is signaled _T("MyEvent") // object name ); hThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFunction,&hEvent,0,&IDThre ad); if (WAIT_TIMEOUT == WaitForSingleObject(hEvent,10000)) cout<<"Time Out"<<endl; WaitForSingleObject(hThread,INFINITE);//This line is used to ensure the cout routine after SetEvent will complete. return 42; }
[C#] [DllImport(@"CPPDLL.dll")] extern static int fnCPPDLL(); static void Main(string[] args) { try { Console.WriteLine(fnCPPDLL()); } catch (Exception e) { Console.WriteLine(e.ToString()); } }
Best regards,
Peter Huang
Microsoft Online Community Support ================================================== When responding to posts, please "Reply to Group" via your newsreader so that others may learn and benefit from your issue. ================================================== This posting is provided "AS IS" with no warranties, and confers no rights.
Paul - 07 Jun 2006 22:37 GMT Hi Peter, Thanks again for your help.... Your sample code is representative of my applications. I created test apps with your code fragments (with the ThreadFunction loop reduced to 5), to find, as you did, that the second thread runs as desired.
The only differences I can see are: 1. You pass in the event handle to the 2nd thread, whereas I create the event (using the same literal) in both the main thread and the 2nd thread.... should be OK though, as it runs when called from a C++ console app? 2. After signalling the main thread, my 2nd thread falls into a windows message loop without terminating. Can't see why this would have any impact?
You have given me some lines of investigation. I will post any progress. Thanks again for your help.
cheers, Paul
> Hi Paul, > [quoted text clipped - 69 lines] > ================================================== > This posting is provided "AS IS" with no warranties, and confers no rights. "Peter Huang" [MSFT] - 08 Jun 2006 04:06 GMT Hi
1. I understand you create a global Event but not pass it via Thread Parameter. Based on my test, it works too.
2. So far I understand that you problem as below. The 2nd thread will be running and signal the event successfully and then loop in the windows message. Now the Main thread should not blocked. Did I miss something?
Also since we did not do complex code review, can you modify the simple test code to reproduce the problem based on your project.
3. I look forward to your troubleshooting steps based on my suggestion.
Best regards,
Peter Huang
Microsoft Online Community Support ================================================== When responding to posts, please "Reply to Group" via your newsreader so that others may learn and benefit from your issue. ================================================== This posting is provided "AS IS" with no warranties, and confers no rights.
Paul - 11 Jun 2006 22:16 GMT Hi Peter, Sorry for the delay in replying, and thanks again for your suggestions. 2. Your description matches the desired functinality. Unfortunately the main thread remains blocked as the 2nd thread does not run and therefore does not signal the main thread.
3. I have modified the simple test code and have succeeded in recreating the failure. I should have mentioned before that the initialisation is being performed when the unmanaged DLL is being loaded and not during a subsequent exported function call.... There appears to be a difference in the threding behaviour of the unmanaged DLL when loaded by an unmanaged C++ console app and when loaded by a C# app utilising PInvoke:
Within the unmanaged DLL, the 2nd thread is created from DllMain when the first process attaches (as there will only be one windows message loop).
[C++ client] This functions as desired when loaded by a C++ console app: the 2nd thread is created, initialisation perfromed, and the main thread is released and returns.
[C# client] When loaded using the InteropServices PInvoke, DllMain() is called as for the C++ console app, the differenece being that the 2nd thread does not run.
Moving the initialisation (creation of the 2nd thread) in DllMain to occur on DLL_THREAD_ATTACH rather than DLL_PROCESS_ATTACH resolves the issue.
To get a full understanding of the root cause I will have to look into the differences in DLL loading.
Thanks again for your help Peter....
Paul
> Hi > [quoted text clipped - 23 lines] > ================================================== > This posting is provided "AS IS" with no warranties, and confers no rights. "Peter Huang" [MSFT] - 13 Jun 2006 06:15 GMT Hi Paul,
Thanks for your quickly reply! I am glad to see that you have resolved the issue, if you still have any concern, please feel free to post here.
Thanks!
Best regards,
Peter Huang
Microsoft Online Community Support ================================================== When responding to posts, please "Reply to Group" via your newsreader so that others may learn and benefit from your issue. ================================================== This posting is provided "AS IS" with no warranties, and confers no rights.
Paul - 07 Jul 2006 09:49 GMT Hi, There is an MSDN article that contains an explanation for the failure observed; follow the link and check out the description under the heading "DllMain Restrictions".
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/ht ml/vcconMixedDLLLoadingProblem.asp
thank you,
Paul
> Hi Paul, > [quoted text clipped - 14 lines] > ================================================== > This posting is provided "AS IS" with no warranties, and confers no rights. "Peter Huang" [MSFT] - 10 Jul 2006 06:36 GMT Hi Paul,
Thanks very much for your knowledge sharing in the community. I believe this will benefit the whole community.
Best regards,
Peter Huang
Microsoft Online Community Support ================================================== When responding to posts, please "Reply to Group" via your newsreader so that others may learn and benefit from your issue. ================================================== This posting is provided "AS IS" with no warranties, and confers no rights.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|