Link to home
Start Free TrialLog in
Avatar of sburck
sburck

asked on

BCB2006 TOpenDialog (and TSaveDialog) problem and missing option

I just got a call from a site where I have an app installed, and a problem was reported to me which I can verify, and cannot find a quick fix.

The problem is using a TOpenDialog (using BCB 2006).  The problem is that drives are not shown - folders, and everything else are shown, but going to "My Computer" gives an empty selection screen.  I couldn't find a flag in Options or OptionsEx which would fix this, either.  I threw a TSaveDialog on the form to test with, it has the same problem.

Avatar of George Tokas
George Tokas
Flag of Greece image

I made an application in BCB2006 and dropped a TOpenDialog and a TButton.
At Button click I called OpenDialog1->Execute() and run it.
After click at the button the dialog displayed with all drives visible when clicked "My Computer"...
Did you installed the updates??
I had installed them...

George Tokas.
Avatar of sburck
sburck

ASKER

No, I found the solution now at Borland -         

"TOpenDialog doesn't show MyComputer items when CoInitFlags := COINIT_MULTITHREADED"

and, because I require CoInitFlags to be COINIT_MULTITHREADED I will have to replace the dialog boxes with API calls.
Agree since I was of no help....
George Tokas.
Avatar of sburck

ASKER

First, I will remove my request to close this question - however, I'm going to change it, and increase it's points, because it has now become considerably more difficult.

After a lot of research, I've found the following:  Although the bug is listed on Borland as I reported above, the 'bug' is in Windows.  See http://support.microsoft.com/kb/287087 (INFO: Calling Shell Functions and Interfaces from a Multithreaded Apartment).  This is not a Borland-specific problem, but in general, a lot of the shell interface will fail from a multithreaded apartment.

Now, this problem cannot be fixed by using API calls, because the API calls fail in the identical manner.  I have to, I believe, make a multithreaded application, where the calls to the DLL I'm using which requires me to use COINIT_MULTITHREADED in one thread, and the other thread is COINIT_APARTMENTTHREADED.  This I don't know how to do.  To make my original application multithreaded I had to add the call to CoInitializeEx(NULL, COINIT_MULTITHREADED); before the Application->Initialize() and then the calls to the DLL worked.  However, I don't know where to go about changing the concurrency model for a TThread.

What I'd like to do, actually, is make the main thread the APARTMENTTHREADED one, and create a MULTITHREADED thread where the DLL calls could go, that would mean the least changes to my code, I think.

Another option is if anyone knows of Open/Save Dialog components which don't care about the concurrency model; I could use these without mucking about with making a rather simple application multithreaded in order to view these two dialogs correctly.

Avatar of sburck

ASKER

Someone has suggested to me to make two applications, one which gets the filenames (normal app), and then runs the second (MULTITHREADED), which display the filenames gotten.  I'm not thrilled with this, but it's my last resort.
The description I'm about to give you assumes that the multithreaded app is running...
Create a new standard app...
Drop a TClientSocket...
At your main application drop a TServerSocket...
Those will be needed for communication the FAST way...
The standard application when starts connecting to the TServerSocket...
The Server sends the data the other app have to find...
The Client sends report...
Precautions:
Because the standard app may called many times from the multithreaded app you have to use a mutex, or simply if you choose TServer/TClient sockets limit the TServer connections by using at OnConnect: if(Socket->ActiveConnections > 1){Socket->Close();}
If something isn't clear on the strategy I'm proposing feel free to ask...

George Tokas.
Avatar of sburck

ASKER

I'll look into this - let me just clarify.  For my user interface, I make a standard application with a clientsocket.  The (main) application which uses the DLL will be formless (or at least invisible), and have a ServerSocket.  The two applications communicate via the sockets.

I'll give this a try and let you know.
Just create a Dialog application with the client socket...
BEFORE the dialog appears HAS TO connect to the server socket...
If not terminating...
If connection closed terminating...
If error terminating...
Choose the file using the dialog and press ok...
At the button's handler communicate with the server and if all are OK terminate the dialog application...

George Tokas.
For saving:
Create a temporary file and send the path and filename to the dialog application...
At the dialog application load the temporary file to a TMemoryStream and save it as you like....
Report to master application for deletion...

George Tokas.
Avatar of sburck

ASKER

It seems that in BDS, the tclientsocket and tserversocket have been deprecated, there are the newer, more complicated INDY 10 components.  I'm looking at how to make a simple client/server connection without any underlying protocol (the old "chat" example from BCB6).
There are there just not added to the pallete...
Check out the bpl TClient/TServer Sockets are included...
Add the same in BDS....
I am on the move and the machines I have the details are not here yet....

George Tokas.
Avatar of sburck

ASKER

After trying to use them (I created them dynamically), they gave strange linker errors.  So, late yesterday I decided to try to use Windows Messaging (PostMessage, with a Message handler on the form of both master and slave to handle a WM_USER message) instead.  I'll keep you updated.
dclsockets.pas have the declarations at BDS...
Try to add the bpl...
They will appeared in the pallete..
Maybe you will find difficulties with post message...
And the reason is multithreading...
In this case you have to use a mutex...

George Tokas.
ASKER CERTIFIED SOLUTION
Avatar of cwwkie
cwwkie

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
From my article at www.bcbjournal.com not yet published but at least I finished moving...

A note about BDS2006. If you noticed TServerSocket and TClientSocket are not present at the Internet “Tab” of components. That does not mean that we can’t use them anymore. The source code and the im-plementation remain there (ScktComp.pas + hpp). So where are they?
 With C++Builder personality open in BDS goto Pro-ject|Options|Packages choose Add and point to BDS\4.0\bin\dclsockets100.bpl. Click ok and now you will see the components at the internet “Tab”.

George Tokas.
Avatar of sburck

ASKER

A couple of comments:

I have begun to write the implementation using Windows Messaging and a mutex, but cannot do anything with it until at the customer site.  The problem DLL won't do anything unless it's hooked up to a big camera which I don't have access to - I'll be there on Monday, so until then I cannot test anything.

However, cwwkie, both of your points make sense.  First thing after posting this comment I'll post a pointer to this question, and won't handle a situation like this again.  Secondly, and more "on subject", the articles showed that it is possible to have different threads with different concurrency models.  This would be the ideal solution.  I would make my application a "Single-threaded-apartment", and create a "multi-threaded apartment" in a thread which calls the DLL.   Synchronization would be simple - all I need to do is wait for the thread to run to completion, after it had called the DLL.  However, I had to place my call to CoInitializeEx(COINIT_MULTITHREADED) before the call to Application->Initialize(), because Application->Initialize() automatically calls CoInitializeEx with COINIT_APARTMENTTHREADED.  I don't know what the case for a tthread is - but I'll wager if I dig into it, the thread will be auto-initialized to COINIT_APARTMENTTHREADED.  If not, or if I can place a call to CoInitializeEx in my constructor which will get called before the class itself calls it, then this may be the way to go.  I'll post this and hope for a definitive answer before Monday, when I will go in and solve the problem any way I can, but as cleanly as possible.
>> I don't know what the case for a tthread is

You don't need to know. Take a look at the code in the second link. You only need to make some small changes, and can use it. It simply calls CreateThread to create a thread. The thread function only needs to contain CoInitializeEx, your own function call and CoUninitialize. Call the Wrapper function to use it.
Avatar of sburck

ASKER

cwwkie - I'm here and working on it - it works to a point.  My problem is callbacks.  The DLL has callback functions, which currently are my functions which update the UI.  This doesn't work - can't update the VCL from other threads.  if I was using TThread, I could call synchronize, but I'm not.  I'm trying to do modifications of the code which instead of calling

WaitForSingleObject (threadHndl, INFINITE)

I'm trying to do:

        while (WaitForSingleObject(threadHndl, 100) == WAIT_TIMEOUT)
        {
             ..update UI via globals set by the callback.
        }

This isn't working.

Any help would be appreciated.

You can take a look what syncronise is doing (source\vcl\classes.pas)

Basicly it does
  SendMessage(ThreadWindow, CM_EXECPROC, 0, LongInt(Self));

In the window procedure, the LParam is typecasted back to a TThread pointer (Self=this) and the function is called.
You cannot use This if you do not use a TThread, but you can create something simular, or create a dummy TThread, just to call Synchronize.

As a simple alternative, you can also use a global variable, and poll it using a ttimer. Be sure to guard the access of the global variable using a mutex or critical section.
Avatar of sburck

ASKER

Due to time constraints, I just finished doing it the dirty way - I used two applications - one invisible and COINIT_MULTITASKING, and the other with the UI and not - and communicated between them using PostMessage between the two forms.  It's not a very clean solution but it works.  I think the way to have gone had there been time is  the way I started today - so I'm giving the points to cwwkie, (gtokas's suggestion had merit, as well, but, it, too, was a two-application solution).  The second article cwwkie gave the pointer to 90% of it, and given time, I would have solved the problem of the callbacks (possibly as you suggested).