Answered Docked application cannot launch SSRS Report

5ilverFox

Member
Joined
Apr 30, 2020
Messages
5
Programming Experience
10+
Hi, I have a C# Windows application (app1) with buttons on a panel for a menu bar. I have done this because the application can also be launched from another application (menu1), that will dock app1 inside a tab control. (A normal toolbar or menu bar becomes non-responsive when the application is docked. (documented bug)). One of the menu buttons in app1 launches a context menu with a list of reports. By running App1 on its own, I can launch any of the reports, which will show in the default browser. However, after menu1 has docked app1 and I launch a report, nothing happens, till I close app1, after which the report will show and dock in the tab control of menu1.

I was wondering if anybody knows of this "serial" behaviour and if there is anything that can be done about it? Is there a way to "break" the link between the app1 process and the report process? Can the parent process be changed on a running process? I will appreciate any ideas from the very clever people out there.

PS: Latest VS, OS = Windows 10, Using .NET Framework 4.
 
Welcome to the forums.

What is this documented bug you talk of? : - (documented bug)

I'm not really understanding your logic here. You can't have applications running inside of applications... I will assume you are talking about forms, and not applications, in which case, you need to be very specific about the functionality you have created and are describing and trying to utilize, and also explain exactly what functionality you are trying to achieve and why. In order for us to help you. You need to draw a complete and clear picture for us to understand. It would also be helpful if you would debug your code and see where exactly it gets stuck, or try to identify why your code stops executing, and you can do this with the debugger. That's what it is for. You can find a debugging tutorial in my signature.

If you manage to identify the relevant code causing the problem you are experiencing, then post that code so we can analyze it and try to identify any possible flaws. If your application is locking up. It's likely due to the fact that you are doing work on your UI thread, which is causing it to be unresponsive. This is just me taking a blind stab in the dark, as your issue may be the result of certain methods not being executed on their own threads.
I have done this because the application can also be launched from another application (menu1)
What? Please explain. What is menu1 and how did you create it?

menu1 is unlikely to be an application. It's likely a control on a separate form. Why would you allow a external application to act as a menu to launch other applications. I'm just going on what you wrote, and that seems like a terribly flawed design.
 
Yes, you can have applications run inside other applications. You launch the 2nd application from the 1st, find the 2nd applications main form and dock it inside a control of the 1st application. So, in my case the 1st application is a C# menu application and the 2nd application is also a C# application. (I could launch notepad as the 2nd application and that would work). Nothing is hanging.

I don't know how to explain it better, but let me try again:
I have 2 applications: A C# windows application (a tree view menu application) and let's call it MENU. The 2nd application is a C# windows application with buttons and a grid and some button can launch an SSRS report and let's call it APP.

If I launch APP directly and click the report button, it will launch the SSRS report as a separate process and show it in a browser as expected.
If I launch MENU and select APP, it will launch APP and dock it inside a new tab in the tab control of MENU. If I now click the report button, it will launch the SSRS report as a third process, but you cannot see it, till you close APP. It must have something to do with changing APP's main form to allow docking. I am just hoping somebody else did something similar and can direct me past this pitfall.

As for the menu bar bug when you dock the application: I don't have the URL's anymore, but it's true and can be easily reproduced.
 
Yes, you can have applications run inside other applications. You launch the 2nd application from the 1st, find the 2nd applications main form and dock it inside a control of the 1st application. So, in my case the 1st application is a C# menu application and the 2nd application is also a C# application. (I could launch notepad as the 2nd application and that would work). Nothing is hanging.
No you can't... Well technically, it is possible by providing the handles of the application you want to use and pass it to your control. But this is really not advised, and unless I am still misunderstanding you, this isn't recommend. So actually it's not so much you can't do it, but it's really not recommended. I'm not sure why you'd want to have two separate windows applications running external to each other when you could just run them as two different forms instead. This requires you use pinvoke and start poking at API's. Have you done something like this? :
C#:
        [DllImport("user32.dll")]
        static extern IntPtr SetParent(IntPtr hWndsub, IntPtr hWndPrime);
        private void button1_Click(object sender, EventArgs e)
        {
            Process new_process = Process.Start("write.exe");
            Thread.Sleep(1000);
            SetParent(new_process.MainWindowHandle, tabControl1.Handle);
        }
I can only imagine if this is your case, you are likely working for a corporate company and this is part of their setup. Because this is the kinda dumb shit corporate companies do. No offense to you or those you might be working for. Just my experience with corporate's over the years, and it reminds me of how they sometimes do things. Often for ridiculous reasons. Rather than editing something and extending onto to the main applications functionality, they instead require you to work with handles instead. Often so other working processes don't break or require replacing. If this is the case, I feel your pain.

If the above is closer to what you're doing, we will need to see how you are calling the other window and controlling it when it's placed in your control. I'can't fathom why you've gone this direction with something which can be majorly simplified by working with individual forms. However, I will help you however I can if this is the direction you need to go.
 
In your example above were you are launching notepad, are you saying that when you close your application, notepad also shutdown?

I think you need to show us your code because in a normal Windows application, you can't launch another application and became the parent window of that other application and have all it's windows docked inside your application. It involves some Win32 API hackery to do that, and it involves even more hackery if your are crossing between 32 and 64-bit borders, or between elevated and non-elevated security contexts.

Yes, you can launch another application from an existing application. That's completely supported by Windows and exactly how the Windows Explorer, the CMD window, PowerShell, Scheduled Task Manager, and other tools work. But they also follow normal Windows conventions and DO NOT make the child applications windows become part of the parent application. There are some exceptions when Windows breaks its own UI rules. For example when you preview a screen saver in the little sample/thumbnail window, Windows actually launches another application -- the screensaver -- and then tells the application to use a specific window handle to draw to so that a preview can happen.
 
In your example above were you are launching notepad, are you saying that when you close your application, notepad also shutdown?
As an aside, that would be normal and expected behaviour if that is the case. By executing the above API code I posted, the window you opened with your main application puts the process you opened under your application name as a child process of your main application.

Screenshot_123.jpg
 
As an aside, that would be normal and expected behaviour if that is the case. By executing the above API code I posted, the window you opened with your main application puts the process you opened under your application name as a child process of your main application.

View attachment 913

Well, we need a central menu system that can launch any application. The problem is actually the other way around. We need to launch 3rd party applications. We are also using this to overcome the caching problem on Terminal Servers. Only 10% of our staff used Terminal Services, but now, during lock down, 99% use it. We don't deploy our programs to the PC's, but rather to a network share. Now on Terminal Server, this causes a problem, because if a user had a program open, every other user that opens that program, will open the cached version. So not till everybody closes that program, will the latest version be picked up, except if the program is on the local drive of the Terminal Server. So we now deploy to the network share and the local drives of the Terminal Servers. This menu detects if it is running on a Terminal Server and will then execute the program on the local drive. This also helps deploying new programs or removing programs. There are other reasons, like cultural, that I will not bore you with.

Oh, and yes, as per your example, that is what I am doing and if you close the menu, all programs opened by it will close and I check that you do not open the same application twice. As in Skydivers example, my problem is when the child program launches another child program. The second child program is invisible till you close the child program at which time the second child shows. So how can I launch the second child and show it, if the child is one of my own C# programs?

OK, giving you that background, here are the code inside the menu that executes the program. MIP is just a struct to remember menu item properties. RealExecPath is where the execution path is changed to the local server, if necessary. There are other clutter, like forcing chrome and firefox to open in new windows, etc. Too much info, too little info?
C#:
       private void Execute()
        {
            MIP mip = (MIP)selectedNode.Tag;
            if (mip.HasExecRights)
            {
                string tabName = "";
                if (mip.ExecPath.Length > 0)
                {
                    string execPath = RealExecPath(mip.ExecPath);
                    Process process = null;
                    //We can execute, but is it running?
                    bool IsProcessRunning = false;
                    DataRow runningRow = GetRunningRow(execPath);
                    if (runningRow != null)
                    {
                        //It thinks it is already running - double check
                        string processName = runningRow["Processname"].ToString();
                        process = mip.RunningProcess;
                        process = GetProcessByName(processName);
                        if (process != null)
                        {
                            IsProcessRunning = true;
                        }
                        if (IsProcessRunning)
                        {
                            //Place focus on running process
                            tabName = runningRow["TabName"].ToString();
                            tab.SelectedTab = tab.TabPages[tabName];
                        }
                    }
                    if (!IsProcessRunning)
                    {
                        IntPtr appWin = IntPtr.Zero;
                        string chromeProfileNo = "";
                        try
                        {
                            //Start the process

                            ProcessStartInfo startInfo = new ProcessStartInfo();
                            if (execPath.StartsWith("http"))
                            {
                                switch (iriUtilities.DefaultBrowser())
                                {
                                    case "ChromeHTML":
                                        if (iriUtilities.IsChromeSSRSExtension())
                                        {
                                            int cnt = 0;
                                            foreach (DataRow row in RunningProcesses.Rows)
                                            {
                                                if (row["ExecPath"].ToString().StartsWith("http"))
                                                {
                                                    cnt++;
                                                }
                                            }
                                            chromeProfileNo = cnt.ToString();
                                            string profileFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).Replace("Roaming", "Local");
                                            profileFolder += @"\Google\chrome\UserData" + chromeProfileNo + @"\";
                                            if (!Directory.Exists(profileFolder))
                                            {
                                                Directory.CreateDirectory(profileFolder);
                                            }
                                            string chromePath = iriUtilities.ChromePath() + "\\chrome.exe";
                                            startInfo.FileName = chromePath;
                                            startInfo.Arguments = execPath + " --user-data-dir=" + profileFolder;
                                        }
                                        else
                                        {
                                            iriUtilities.DisplayChromeMessage();
                                        }
                                        break;
                                    case "FireFox":
                                        string fireFoxPath = iriUtilities.FireFoxPath() + "\\forefox.exe";
                                        startInfo.FileName = fireFoxPath;
                                        startInfo.Arguments = execPath + " -new-window";
                                        break;
                                    default:
                                        startInfo.FileName = execPath;
                                        break;
                                }
                            }
                            else
                            {
                                startInfo.FileName = execPath;
                            }
                            startInfo.WindowStyle = ProcessWindowStyle.Minimized;
                            process = new Process();
                            process.StartInfo = startInfo;
                            bool started = false;
                            try
                            {
                                started = process.Start();
                            }
                            catch
                            {
                                //Because some apps cannot start minimized
                                process.Close();
                                startInfo.WindowStyle = ProcessWindowStyle.Normal;
                                process = new Process();
                                process.StartInfo = startInfo;
                                started = process.Start();
                            }
                            Thread.Sleep(500);
                            while (process == null || process.MainWindowHandle == IntPtr.Zero || process.Id <= 0)
                            {
                                process.WaitForInputIdle();         //To check if process has started
                                Thread.Sleep(500);
                                Application.DoEvents();
                            }
                            try
                            {
                                Process p = Process.GetProcessById(process.Id);         //To double check if process has started
                                while (p == null)
                                {
                                    Thread.Sleep(500);
                                    p = Process.GetProcessById(process.Id);         //To double check if process has started
                                }
                            }
                            catch (InvalidOperationException)
                            {
                                started = false;
                            }
                            if (started)
                            {
                                mip.Running = true;
                                mip.RunningProcess = process;

                                //Create a new tab and dock process in it
                                int cnt = RunningProcesses.Rows.Count + 1;
                                tabName = "TabPage" + cnt.ToString();
                                tab.TabPages.Add(tabName, selectedNode.Text);
                                Thread.Sleep(500);
                                TabPage tabPage = null; ;
                                while (tabPage == null || tabPage.RecreatingHandle)
                                {
                                    Application.DoEvents();
                                    Thread.Sleep(500);
                                    tabPage = tab.TabPages[tabName];
                                }
                                Application.DoEvents();
                                //Find main window
                                while (appWin == null || appWin == IntPtr.Zero)
                                {
                                    Thread.Sleep(300);
                                    appWin = process.MainWindowHandle;
                                }
                                process.EnableRaisingEvents = true;
                                Thread.Sleep(300);
                                process.Exited += new EventHandler(Process_Exited);
                                Thread.Sleep(300);
                                startInfo.WindowStyle = ProcessWindowStyle.Maximized;
                                Thread.Sleep(300);

                                //Place process into tabPage
                                SetParent(appWin, tabPage.Handle);
                                SetWindowLong(appWin, GWL_STYLE, WS_VISIBLE & ~WS_CAPTION & ~WS_BORDER & ~WS_CHILD);
                                int wh = tabPage.Height;
                                MoveWindow(appWin, 0, 0, tabPage.Width, wh, true);

                                //Make new tab the focus
                                tab.SelectedTab = tab.TabPages[tabName];
                                //Add new process to RunningProcess
                                RunningProcesses.Rows.Add(GetDataRow(execPath, process.ProcessName, tabName, selectedNode.Name, chromeProfileNo, process.Id.ToString()));
                            }
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.Message);
                        }
                    }
                }
            }
        }
 
Last edited by a moderator:
OMG! The code is riddled with the window handle manipulation hacks.

And even worse there are multiple calls to Application.DoEvents(). How do you know that you are not accidentally re-entering your program when you call Application.DoEvents() and causing a deadlock, and hence the grandchild processes that are being launched are freezing?

Just to make sure I understand correctly, you have this master menu application (menu1). The user can choose to launch a custom C# application (app1) that is completely under your control. Furthermore, that child custom C# application (app1) can launch another custom C# application (SSRS app) -- the grandchild. And it is this grandchild which is not visible or is freezing until the child application (app1) is terminated.
C#:
menu application (menu1)
+---- custom C# application 1  (child application: app1)
      +---- custom C# application 2 (grandchild application: SSRS app)
 
But they also follow normal Windows conventions and DO NOT make the child applications windows become part of the parent application.
Normally they don't. With exception to what you just wrote, it does if you use the API call I wrote above, which also happens to be the same code used by @5ilverFox. And as I've demonstrated in my previous screenshot, this is a problem that they developed by misusing windows API calls which set independent window and thus makes them a child of the calling API application - for where that handle is set. That's what this API does. So of course any application called with SETPARENT API will become a child process of the calling code when used in this way. However as an aside note. That's not what I believe is locking up the other application. I believe that you may have established the reason below where I quoted your response.
Oh, and yes, as per your example, that is what I am doing and if you close the menu, all programs opened by it will close and I check that you do not open the same application twice.
It has nothing to do with the same application opening twice. The SETPARENT API is subsequently running it under your calling code. This can be seen by simply using the windows task manager. Your SETPARENT call runs the launched process as a child process under the calling code when you set the handle for the tabcontrol as I've demonstrated already. See these docs SetParent function (winuser.h) - Win32 apps and take note of the line :
An application can use the SetParent function to set the parent window of a pop-up, overlapped, or child window.
So remind me why you are using this API and why you thought it be a good idea to use this knowing that it changes the parent to be a child of the calling code when you set the handle of the control to be your second or third application?
I'm genuinely confused. You really would have been better using one application with multiple forms or use form models instead.
And even worse there are multiple calls to Application.DoEvents().
On this note; and in respect of Application.DoEvents, you should note the following quote from Microsoft :
While your code handles the event, your application does not respond. For example, the window does not repaint if another window is dragged on top.
This may be what is causing your "hangs". See the remarks for : Application.DoEvents Method (System.Windows.Forms) To wrap up here. -

I think you have quite a lot of debugging and code cleaning to do. There may also be additional API's you can use to try to help with handling other windows sitting on top of your main window while using Application.DoEvents. Just what they might be, I'm not quite sure. I'd sincerely recommend changing your approach.
 
I second that!

Or if a master menu application is a must, then load the other child applications as add-ins. Take advantage of MEF so that you don't have to roll your own add-in architecture.
 
I second that!

Or if a master menu application is a must, then load the other child applications as add-ins. Take advantage of MEF so that you don't have to roll your own add-in architecture.
In my case that is not an option, because some of the applications are 3rd party applications. Only the application I am having a problem with, is a C# application that I can change.

Your comments about DoEvents are noted, but in some cases it is relevant, for instance: When you launch a 3rd party application and wish it to display it's main window, DoEvents can help giving the 3rd party application time to do so. That said, it does not influence my application at all, because it behaves as always when all the DoEvents were removed, but I will leave them removed.

Somebody, and for the life of me I cannot find the reference, said that you cannot launch an application from a docked application. Taking that as true, I have changed the child C# application to not launch any grandchildren. Thanks for the pointers and although it is not the solution I was looking for, it is now working.

PS: I have seen this done at another company that use DevExpress and they have zero issues. The DevExpress controls just work. Too bad I cannot afford them. :(
 
Back
Top Bottom