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:
Selecting a project in the list view and clicking the 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:
Check to see if WinProj.exe is in the process list
If it IS, then grab an instance and check the active profile server URL against the browser’s server URL
- The two match, open the project programmatically
- The two do not match, bail…
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.
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
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:
Comments
- Anonymous
January 09, 2009
PingBack from http://www.codedstyle.com/opening-an-enterprise-project-programmatically/