Opening an Enterprise Project Programmatically

There must be a better way to do this.  I have a Project Server plugin for the mpFx client application:

image 

Selecting a project in the list view and clicking the image button opens the project in WinProj.  The problem is that you cannot open a project that exists in one project server instance programmatically if the user has WinProj open and connected to another instance of Project Server.  Through automation, it is easy to determine which  server the user is currently connected to:

    1: _Winproj.Profiles.ActiveProfile.Server

If the user has WinProj open, I can grab an automation instance and check the current profile server against the server that my browser is currently pointed to.  If the two don’t match, I indicate to the user that they must close WinProj and try again. 

If WinProj is running and the user is connected to the same instance of WinProj, we simply open the project programmatically. 

The challenge is to roll these different scenarios into one and solve the whole problem.   Here is the basic layout of the method:

  1. Check to see if WinProj.exe is in the process list

  2. If it IS, then grab an instance and check the active profile server URL against the browser’s server URL

    1. The two match, open the project programmatically
    2. The two do not match, bail…
  3. If it IS NOT, then start WinProj.  In my case, I have 6 possible candidate Project Server instances to choose from.  I don’t have a default profile so starting WinProj doesn’t automatically log me in. 

  4. Assuming WinProj started, wait for the user to login.  If WinProj is busy and I attempt to automate it in anyway, you will see good old RPC_E_CALL_REJECTED exceptions

  5. Once WinProj accepts calls, I can check to see if the Project Server instance the user chose to login to matches and complete the circuit.

Here it is:

    1: private void OpenSelectdProjectInWinProj()
    2:  {
    3:      Process[] processes = Process.GetProcessesByName("winproj");
    4:  
    5:      bool success = false;
    6:  
    7:      if (processes.Length == 0)
    8:      {
    9:          //  No WinProj in the proclist.  Cook one up.
   10:          ProcessStartInfo startupInfo = new ProcessStartInfo
   11:                                             {
   12:                                                 FileName = "winproj.exe",
   13:                                                 Arguments = string.Format(" /s {0}", _Parent.ProjectServer.Site)
   14:                                             };
   15:          try
   16:          {
   17:              Process.Start(startupInfo);
   18:              Thread.Sleep(DEFAULT_WAIT_TIME);
   19:          }
   20:          catch (Win32Exception exception)
   21:          {
   22:              MessageBox.Show(this,
   23:                              exception.Message,
   24:                              _Parent.Name,
   25:                              MessageBoxButtons.OK,
   26:                              MessageBoxIcon.Error);
   27:              return;
   28:          }
   29:      }
   30:  
   31:      for (int i = 0; i < DEFAULT_RETRY_COUNT; i++)
   32:      {
   33:          //  If an instance of WinProj was created above, we are waiting for the user to login, 
   34:          //  during which time the call below will generate RPC_E_CALL_REJECTED exceptions 
   35:          try
   36:          {
   37:              _Winproj = new Application();
   38:  
   39:              _Winproj.Visible = true;
   40:              //  Okay, we have an instance of WinProj and we can talk to it.
   41:              break;
   42:  
   43:          }
   44:          catch (COMException exception)
   45:          {
   46:              Debug.WriteLine(exception.Message);
   47:  
   48:              //  Free the automation object so the ref count doesn't explode
   49:              FreeWinProj();
   50:  
   51:              Thread.Sleep(DEFAULT_WAIT_TIME);
   52:          }
   53:      }
   54:  
   55:      if (_Winproj != null)
   56:      {
   57:          //  We may have an automation object that was previously speaking to us, but it appears 
   58:          //  that calls will be rejected for a few more seconds after we squeaked through in the loop above.
   59:          
   60:          Exception exception;
   61:  
   62:          for (int i = 0; i < DEFAULT_RETRY_COUNT; i++)
   63:          {
   64:              try
   65:              {
   66:                  success = _Winproj.Profiles.ActiveProfile.Server.Equals(_Parent.ProjectServer.Site.ToString(),
   67:                                                                          StringComparison.CurrentCultureIgnoreCase);
   68:                  //  We have determined whether the two urls match
   69:                  break;
   70:              }
   71:              catch (COMException e)
   72:              {
   73:                  exception = e;
   74:              }
   75:              catch (InvalidCastException e)
   76:              {
   77:                  exception = e;
   78:              }
   79:  
   80:              if (exception.Message.Contains("not available"))
   81:              {
   82:                  //  The RCW got disconnected from the automation object, so it is likely the user 
   83:                  //  closed project.  Bail...
   84:                  break;
   85:              }
   86:              Debug.WriteLine(exception.Message);
   87:              Thread.Sleep(DEFAULT_WAIT_TIME);                    
   88:          }
   89:      }
   90:  
   91:      //  We are de-looped and now we can test to see if we should open the project
   92:      if (_Winproj != null && success)
   93:      {
   94:          _Winproj.FileOpenEx(@"<>\" + projectsListView.SelectedItems[0].Text,
   95:                              false,
   96:                              Type.Missing,
   97:                              Type.Missing,
   98:                              Type.Missing,
   99:                              Type.Missing,
  100:                              Type.Missing,
  101:                              Type.Missing,
  102:                              Type.Missing,
  103:                              Type.Missing,
  104:                              Type.Missing,
  105:                              Microsoft.Office.Interop.MSProject.PjPoolOpen.pjDoNotOpenPool,
  106:                              Type.Missing,
  107:                              Type.Missing,
  108:                              Type.Missing,
  109:                              Type.Missing,
  110:                              Type.Missing);
  111:      }
  112:      else
  113:      {
  114:          MessageBox.Show(this,
  115:                          "You may be logged onto a different instance of Project Server.  Please close Project and try again.",
  116:                          _Parent.Name,
  117:                          MessageBoxButtons.OK,
  118:                          MessageBoxIcon.Error);                
  119:      }            
  120:  }

Here is a vide of it failing because I am already logged into a different instance of Project Server

And here is the final product: