Can someone explain to me what this error I'm seeing is?
Current thread must be set to single thread apartment (STA) mode before OLE calls can be made.
Specifically, I'm trying to open the SaveFileDialog/OpenFileDialog within C++/CLI on a form.
SaveFileDialog^ saveFileDialog1 = gcnew SaveFileDialog;
saveFileDialog1->ShowDialog();
if (saveFileDialog1->ShowDialog() == System::Windows::Forms::DialogResult::OK)
{
s = saveFileDialog1->OpenFile();
}
s->Close();
}
The error that is throwing is
An unhandled exception of type 'System.Threading.ThreadStateException' occurred in System.Windows.Forms.dll
Additional information: Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it. This exception is only raised if a debugger is attached to the process.
I'm not really familiar with what this error is saying. I know just a bit about threading, but I'm not sure how threading would be an issue here. I've seen some people reference things like STAThread without providing a clear explanation as to what it does, and Microsoft's documentation makes no mention of having this exception thrown when calling SaveFileDialog/OpenFileDialog, or how to handle it.
Thanks!
When you use OpenFileDialog then a lot of code gets loaded into your process. Not just the operating system component that implements the dialog but also shell extensions. Plugins that programmers write to add functionality to Windows Explorer. They work in that dialog as well. There are many, one you are surely familiar with is the extension that makes a .zip file look like a folder.
One thing Microsoft did when they designed the plug-in interface is to not force an extension to be thread-safe. Because that is very hard to do and often a major source of bugs. They made the promise that the thread that creates the plugin instance is also the thread on which any call to the plugin is made. Thus ensuring that the plugin is always used in a thread-safe manner.
That however requires a little help from you. You have to make a promise that your thread, the one that calls OpenFileDialog::Show(), observes the requirements of a single-threaded apartment. STA for short. You make the promise with the [STAThread] attribute on your program's Main() entrypoint. Or if it is a thread that you created yourself then by calling Thread::SetApartmentState() before you start it.
That's just a promise however, you also have to implement what you promised. Takes two things, you promise to never block the thread and you promise to pump a message loop. Application::Run() in a .NET program. The never-block promise ensures that you won't cause deadlock. And the message loop promise says that you implement a solution to the producer-consumer problem.
This should never be a problem, it is very unclear how this got fumbled in your project. Another implicit requirement for a dialog is that it must have an owner. Another window on which it can be on top of. If it doesn't have one then there are very high odds that the user never sees the dialog. Covered by another program's window, the user can only ever find it back by accident. When you create windows then you always also must call Application::Run() so the windows can respond to user input. Use the boilerplate code in a C++/CLI app so this is done correctly.
Related
From msdn it seems that IMessageFilter doesn't handle all exceptions, for example, at some points, office applications 'suspend' their object model, at which point it cannot be invoked and throws: 0x800AC472 (VBA_E_IGNORE)
In order to get around this, you have to put your call in a loop and wait for it to succeed:
while(true)
{
try
{
office_app.DoSomething();
break;
}
catch(COMException ce)
{
LOG(ce.Message);
}
}
My question is: if this boiler-plate code is necessary for each call to the office app's object model, is there any point in implementing IMessageFilter?
Yes, the two mechanisms are independent. IMessageFilter is a general COM mechanism to deal with COM servers that have gone catatonic and won't handle a call for 60 seconds. The VBE_E_IGNORE error is highly specific to Excel and happens when it gets in a state where the property browser is temporarily disabled. Think of it as a modal state, intentionally turned on when Excel needs to perform a critical operation that must complete before it can handle out-of-process calls again. A lock if you will. It is not associated with time, like IMessageFilter, but purely by execution state. Geoff Darst of the VSTO team gives some background info in this MSDN forums thread.
Having to write these kind of retry loops is horrible of course. Given that it is an execution state issue and assuming that you are doing interop with Excel without the user also operating the program, there ought to be a way to sail around the problem. If you are pummeling Excel from a worker thread in your program then do consider reviewing the interaction between threads in your code to make sure you don't create a case where the threads are issuing Excel calls at roughly the same time.
I'm using 3.9.7 cURL library to download files from the internet, so I created a dynamic bibioteca of viculo. dll written in C using VC + + 6.0 the problem is that when either I call my function from within my vb6 application window locks and unlocks only after you have downloaded the file how do I solve this problem?
The problem is that when you call the function from your DLL, it "blocks" your app's execution until it gets finished. Basically, execution goes from the piece of code that makes the function call, to the code inside of the function call, and then only comes back to the next line after the function call after the code inside of the function has finished running. In fact, that's how all function calls work. You can see this for yourself by single-stepping through your code in the VB 6 development environment.
You don't normally notice this because the code inside of a function being called doesn't take very long to execute before control is returned to the caller. But in this case, since the function you're calling from the DLL is doing a lot of processing, it takes a while to execute, so it "blocks" the execution of your application's code for quite a while.
This is a good general explanation for the reason why your application window appears to be frozen. A bit more technically, it's because the message pump that is responsible for processing user interaction with on-screen elements is not running (it's part of your code that has been temporarily suspended until the function that you called finishes processing). This is a bit more difficult for a VB programmer to appreciate, since none of this nitty-gritty stuff is exposed in the world of VB. It's all happening behind the scenes, just like it is in a C program, but you don't normally have to deal with any of it. Occasionally, though, the abstraction leaks, and the nitty-gritty rears its ugly head. This is one of those cases.
The correct solution to this general problem, as others have hinted at, is to run lengthy operations on a background thread. This leaves your main thread (right now, the only one you have, the one your application is running on) free to continue processing user input, while the other thread can process the data and return that processed data to the main thread when it is finished. Of course, computers can't actually do more than one thing at a time, but the magic of the operating system rapidly switching between one task and another means that you can simulate this. The mechanism for doing so involves threads.
The catch comes in the fact that the VB 6 environment does not have any type of support for creating multiple threads. You only get one thread, and that's the main thread that your application runs on. If you freeze execution of that one, even temporarily, your application freezes—as you've already found out.
However, if you're already writing a C++ DLL, there's no reason you can't create multiple threads in a VB 6 app. You just have to handle everything yourself as if you were using another lower-level language like C++. Run the C++ code on a background thread, and only return its results to the main thread when it is completely finished. In the mean time, your main thread is free.
This is still quite a bit of work, though, especially if you're inexperienced when it comes to Win32 programming and the issues surrounding multiple threads. It might be easier to find a different library that supports asynchronous function calls out-of-the-box. Antagony suggests using VB's AsyncRead method. That is probably a good option; as Karl Peterson says in the linked article, it keeps everything in pure VB 6 code, which can be a real time saver as well as a boon to future maintenance programmers. The only problem is that you'll still have to process the data somehow once you obtain it. And if that's slow, you're right back where you started from…
Check out this article, which demonstrates how to asynchronously transfer large files using a little-known method in user controls.
I'm writing a dll library in Delphi with multiple threads created by it. Let me describe the problem step by step. I'm sorry for a lengthy description in advance :-(.
Let's forget about the library for a while. I created a windows application that is going to present views from several cameras. I created a window which is meant to show the view from a single camera and it contains a TImage control. There is a thread (a TThread descendant) that downloads the current image from the camera every couple of milliseconds and assigns it to the TImage control of that window (using the Synchronize() method). The application creates several instances of that window on startup (with a separate thread for each of them), so you can see the live view from several cameras at once. What's more, all those viewing windows are parented by the main application window, so they appear within it.
Everything worked fine until I decided to put those two windows into a dll library. I just found it necessary for some reasons, but they are not important now. So I created a new dll library, added the existing main window and the camera-view window to the project and exported a function that creates and returns an instance of the main window. When the main window is created, it creates several camera-view windows, making itself their parent.
Then, for testing purposes, I created an app that imports the above mentioned dll function from the library and calls it at startup to get an instance of the main window; then just shows it on the screen (in a non-modal state).
When I started the app it came out that I couldn't get a single image from any camera then. When I debugged it, I noticed that when the thread calls the Synchronize() method, it hangs forever. It didn't happen before putting both those windows into a dll.
And this is my problem. To be honest, this is my first approach to libraries I have had to get through many other problems so far. You might wonder why I use windows instead of frames... So whenever I created an instance of a TFrame in a dll, I would get an exception saying "the control xxx does not have a parent window". I did not know what to do about that so I used windows instead :-(.
Could you please tell me what to do with the synchronization problem? The main thread does not seem to be blocked in any way when the application is started for it accepts clicking buttons etc. What is the problem then?
Please, help!
Thank you in advance!!
When you call TThread.Synchronize the thread and method pointer are added to a global SyncList: TList in Classes.pas. In the main exe's TApplication.Idle routine calls CheckSynchronize, which checks the SyncList, but it's going to check the version in the exe instead of the one in the DLL. End result, your synchronized methods are never called.
The easiest fix would be to switch from DLLs to packages, which would eliminate the duplicate SyncList.
Another approach would be to override the exe's Application.OnIdle callback, and call your DLL's CheckSynchronize manually. You would need some help from the application for that though, since your DLL will have an Application object too, and that one won't work.
It's a bad idea to use Synchronize, because it tends to lead to race conditions like this. I don't know what's going on specifically in your code--it's hard to tell without seeing any code--but this sort of issue is pretty common actually.
Inter-thread communication is better done with a queue. If you've got the latest version, Delphi XE, there's a TThreadedQueue<T> class in Generics.Collections that's ideal for this sort of thing. Pass 0 to the PopTimeout param in the constructor, have your camera threads push images, and have your main thread poll the queues with the third PopItem overload, like so:
var
CurrentItem: TImage;
begin
if ThreadQueue.PopItem(CurrentItem) = wrSignaled then
UpdateImage(CurrentItem); //or however you do it
end;
(If there's nothing in the queue, PopItem will return wrTimeout instead.)
If you don't have Delphi XE, you'll need to build your own threadsafe queue, or find one from a third party source, such as Primoz Gabrielcic's OmniThreadLibrary.
I found two ways to solve Synchronize() hanging up the thread (in Delphi 7):
Place a TTimer on the DLL form and have its OnTimer event call CheckSynchronize();
procedure TPluginForm.Timer1Timer(Sender: TObject);
begin
CheckSynchronize;
end;
Add this module to the uses section of the DLL form
I have a multi-threaded Delphi 6 Pro application that I am currently working on heavily. If I set a breakpoint on any code that runs in the context of the Main thread (VCL thread) I don't have any problems. However, if a breakpoint is triggered on any code in one of my other threads, after I continue the application from the breakpoint, all repaints to the VCL components on the main thread (including the main form) don't happen anymore. The application isn't dead because other background code keeps running, just the main thread. It's as if the windows message dispatcher has been corrupted or rendered dormant.
Note, in this application I allocate my own WndProc() via allocateHwnd() on the main form because I need to catch certain registered messages. From that WndProc() I dispatch any custom messages I handle and if the current message is not handled by my code, I pass the message on by calling the main form's inherited WndProc(). If I do handle the current message I simply return from my WndProc() with Msg.Result set to 1 to tell the dispatcher that the message was handled. I can't simply override the TForm WndProc() instead of allocating my own WndProc() because for some reason the Delphi VCL does not pass through registered messages instantiated with the Windows API RegisterWindowMessage() call.
Has anybody experienced this in similar context and if so, what did you do to fix it?
-- roscherl
Since you call AllocateHWnd, that means you've created another window. You mustn't just take the messages that were addressed to that window and forward them to your form's window. Doing that, you're bound to screw things up in your program, although I'm not sure exactly how. Painting problems sound plausible. You should make sure it's really just painting problems and not that your main thread is still suspended. The debugger should be able to tell you that. (You should call DefWindowProc to make your allocated window handle messages you're not prepared to handle yourself. And returning 1 doesn't tell the dispatcher anything; the dispatcher doesn't care — whoever called SendMessage wants to know the result.)
I promise you that forms are completely capable of receiving registered window messages. Override WndProc or assign a new value to the WindowProc property (and remember to save the old value so you can call it after handling your own messages). The source of your problem lies elsewhere.
UPDATE: I'm not saying the way I got past the problem is a good solution. I need to take Rob Kennedy's notes and do some refactoring. However, to get past the problem for now I gave the thread it's own Window and WndProc() and at the top of the thread Execute loop I have a PeekMessage() while loop with calls to TranslateMessage() and DispatchMessage(). I no longer have a problem with setting breakpoints in the thread, but obviously this compounding of WndProc() methods indicates a structural problem in my code. I wanted to add this reply to fill out the discussion. I'm hoping that once I put Rob's suggestions to work when I clean up my WndProc() methods on the relevant forms, especially the main form, I can get rid of the this new WndProc() that I just added to the thread.
Robert.
I have a worker thread in a class that is owned by a ChildView. (I intend to move this to the Doc eventually.) When the worker thread completes a task I want all the views to be updated. How can I make a call to tell the Doc to issue an UpdateAllViews()? Or is there a better approach?
Thank you.
Added by OP: I am looking for a simple solution. The App is running on a single user, single CPU computer and does not need network (or Internet) access. There is nothing to cause a deadlock.
I think I would like to have the worker thread post (or send) a message to cause the views to update.
Everything I read about threading seems way more complicated than what I need - and, yes, I understand that all those precautions are necessary for applications that are running in multiprocessor, multiuser, client-server systems, etc. But none of those apply in my situation.
I am just stuck at getting the right combination of getting the window handle, posting the message and responding to the message in the right functions and classes to compile and function at all.
UpdateAllViews is not thread-safe, so you need to marshal the call to the main thread.
I suggest you to signal a manual-reset event to mark your thread's completion and check the event's status in a WM_TIMER handler.
suggested reading:
First Aid for the Thread-Impaired:
Using Multiple Threads with MFC
More First Aid for the Thread
Impaired: Cool Ways to Take Advantage
of Multithreading