Project Programmability

This blog focuses on customizations and programming for Project Web App, Project Server, Project Professional and Project Standard. Includes User Interface (UI) customizations, Project Server Interface (PSI) and Visual Basic for Applications (VBA) Programming. It also covers Business Intelligence.

  • Project Programmability and Business Intelligence

    Project Server 2013 reporting database (RDB) schema reference


    The attached Project2013Reporting.exe file contains a draft release of the schema reference for the reporting tables and views in Project Server 2013. To install the files, run Project2013Reporting.exe. The next release of the Project 2013 SDK download will also include the reporting schema reference; that will be announced here when the Project 2013 SDK update is published.

    The Project Server 2013 RDB and OLAP cubes are accessible only with an on-premises installation of Project Server. For Project Online, you can use REST queries of the ProjectData OData service. The ProjectData service is also available with on-premises installations. For more information, see ProjectData - Project 2013 OData service reference in the Project 2013 SDK.

    The attached file includes the following:

    • ProjectServer2013_ReportingDB.chm is the HTML Help build of the RDB reference. It includes only the tables, views, stored procedures (SProcs), and user-defined functions (UDFs) that are in the dbo user namespace of the Project Server database. 

    Note: The draft, published, and archive database objects are not documented. Project Server 2013 combines objects from the four Project Server 2010 databases into one database. The default name of the database is ProjectService; different Project Web App instances can have different database names.

    • OLAPCubeSchemas2013.xlsx includes a worksheet for each of the 14 OLAP cubes that are available to build with Project Server 2013. Each worksheet lists the dimensions, measures, and properties that are available in the cube.

    The Project Server 2013 RDB includes three new tables, 27 new views, and a total of 33 tables and views that include new fields. You can easily find the new items by searching, for example, for “New field” (with quotes). The following screenshot shows the new ProjectVisibilityMode field in the dbo.MSP_EpmProject_UserView view:


  • Project Programmability and Business Intelligence

    Agile Custom Project Guide


    In 2003, we released an Agile custom Project Guide, based on MSF for Agile Software Development methodology (which is scenarios driven , context based, Agile software development process).  We have been able to get it working for Project 2007 as well.  The Agile Project Guide allows the project manager to easily implement the Agile methodology for their projects, by outlining the Agile process and offering templates for process documentation.

    The Agile Project Guide integrates with Microsoft Office Project 2007 in order to manipulate tasks within the project plan itself and provides context sensitive task/resource information in the project guide panel.


    Here is what you need to do to get the Agile custom Project Guide working:

    1.      Download the attached zip file that contains the Agile Custom Project Guide files to your local machine

    2.      Launch Project 2007 client,

    3.      Enable the Project Guide from the View menu or just go to Tools | Options | Interface and select the “Display Project Guide” checkbox

    From the Tools | Options menu | Interface tab

    4.      Select the “Use a custom page” radio button (from the “Project Guide Functionality and Layout Page:” section)

    a.      Click the Browse button and point to the mainpage.htm file, in the Agile Project Guide folder you’ve copied locally in step 1

    5.      Select the “Use custom content” radio button (from the “Project Guide Content:” section)

    a.      Click the Browse button and point to the AgileGuide.xml file, in the Agile Project Guide folder you’ve copied locally in step 1

    6.      Click Ok on the main dialog

    7.      You should now see the Agile Custom Project Guide






    Due to the size of the Zip file, I need to split the file into two. This post has the first file. I will do a second post that will have the second file attached.


  • Project Programmability and Business Intelligence

    Statusing Transactions (Partial Documentation)



    Unlike the project Web Access “My Tasks” web part, the Statusing web service API restricts team member task assignment updates that combine with the create/reassign action to one per approval cycle. This means that once a create/reassign plus updated status is submitted it cannot be further updated until accepted or rejected by the Project Manager.

    This limitation is caused by the absence of “Node Consistency” functionality in the API. This component ensures that the task is kept schedule consistent during team member updates. The project Server 2007 architecture placed this component on the Project Web Access web front end server, rather than in the Project Server Interface business object layer, so as to deliver a responsive user experience in the UI.

    Unfortunately the application developer who is tasked with synchronizing status with an external system (such as an ERP or CRM system) has no means to detect this “blocked” status. We plan to add such a method in an upcoming release of Project Server. In the interim this documentation can be used to extend the Project Server Interface to add a web service/method to deliver the status update status.

    Note: This document contains interim documentation for tables in the Published database of a RTM-edition Project Server 2007 installation. Entities in this schema are subject to change without notice. This documentation is made available on a “best efforts” basis, Microsoft Product Support Services have not been trained in its usage and will not offer technical support for issues related to custom queries against this schema. This schema should not be updated by custom code, any updates to the data may break Project client cache code.

    Extending the Project Server Interface

    This is documented in the following Project Server 2007 Software Development Kit article.

    Note that any code will be required to connect to the Project Server 2007 Published database – in a single site farm this may safely be hard coded, however in a multi-site farm it is recommended that the developer implement a method for connecting to the correct published database, suggestions include:

    • Using the SharePoint site-id to index into an INI file with the correct connection string stored as an external string
    • Passing the database (and SQL Server name) as method arguments.

    Understanding the MSP_ASSIGNMENT_TRANSACTIONS Table

    All status updates are stored in the MSP_ASSIGNMENT_TRANSACTIONS table together with flags indicating the current state of the update. The flags necessary to locate a blocked task assignment are documented below.


    There are four ENUM fields that govern state, these are:

    Attribute Value Enumeration
    Not Submitted
    Submitted Pending Approval
    Approved or Rejected
    Task Assignment Update
    Declined Task Assignment
    Create Task Request
    Delegate Task Assignment Request
    Create Task Assignment Request
    Team Delegation Request
    Delete Task Request
    Delete Task Assignment Request
    InvalidUpdate; InvalidDelegation

    So a blocked task assignment and will have ASSN_TRANS_STATE_ENUM = 1 (pending approval) and ASSN_TRANS_TYPE_ENUM = 2,3,4 (indicating a pending task assignment add/change)

    SQL Server query to test the status of a particular task assignment:
    WHERE ASSN_UID = '617633E6-8B2A-4620-BCAD-F82A95AD398D' -- Assignment UID 

    If the task assignment status update is not blocked then no data will be returned; if it is blocked then a scalar value of integer 1 will be returned.

    See comments in the performance section before changing this query.

    Security Considerations

    PSI Extensions are responsible for implementing their own security. In this case there are mitigations which mean that additional permissions checks may be unnecessary:

    • All callers must be authenticated users making anonymous DoS attacks impossible
    • The proposed solution returns a single numeric result that indicates if the assignment is blocked (1) or not (null)
    • The argument is a UUID – these are very hard to predict/guess so it isn’t possible to cycle through a sequence hunting for hits
    • If an attacker discovers that a particular assignment UID is blocked then there is little they can do with that information
    • The call is extremely performant

    Note that it would be simple to make the call as the “interface user” and test for that in the PSI extension should further restrictions be required.

    Performance Notes

    The above query has been optimized for performance – strictly speaking the query is stronger than required as only the second task assignment status update is blocked by the pending approval. However checking for this first pending update would require the addition of a self-join to the query which would reduce performance considerably. This would test for the above condition AND a pending task assignment status update (ASSN_TRANS_TYPE_ENUM = 0). Provided the work is added in the same ChangeXML payload that changes the task assignment, it is superfluous.

    It is recommended that a query covering index be created on the MSP_ASSIGNMENT_TRANSACTIONS table to avoid both clustered index scans, secondary index scans or bookmark lookups as the table is indexed on a non-sequential UUID (so data access will be very random).

    The following index definition is suggested:

      [ASSN_UID] ASC, 
    ) ON [PRIMARY]

    The sample query (above) uses a TOP 1 clause to further reduce the SQL Server results processing (either no rows or a scalar result set will be returned).

    If you alter the query to return more data, or to use different search arguments then this index definition might need to change.

    Note that creating a stored procedure and using output parameters would further improve performance.


    Patrick Conlan

  • Project Programmability and Business Intelligence

    Working with Deliverables


    The other day I had a request from an internal customer that wanted to create deliverables for a large number of tasks that already existed in their project plan. They wanted to be able to simply flag each task as a deliverable and have it published. They did not want to do all the steps involved with creating a deliverable. 

    They also wanted to tightly couple the task name and dates with the deliverable name and dates. Currently, if a deliverable is linked to a task, when the task’s dates change, the dates for the deliverable do not. This is by design to allow the project manager to intentionally make the change to the deliverable dates since these dates are commonly published to a large audience. In this case, the user wanted the deliverable dates to change with the task dates with minimum user intervention.

    To get started, I created a flag enterprise custom field. The custom field that I created was "Pub Deliverable" and it is a task custom field. I added the field to the Gantt Chart view in Project Professional:

    Next, I wrote the following VBA macro:

    Sub Create_Flagged_Tasks_As_Deliverables()
        Dim t As Task
        Dim fPub As String

        For Each t In ActiveProject.Tasks

            ' This gets the flag value from the Enterpise Custom Field
            fPub = t.GetField(FieldNameToFieldConstant("Pub Deliverable"))
            If fPub = "Yes" Then
                ' If the task has this deliverable GUID, then there is no deliverable
                If t.DeliverableGuid = "00000000-0000-0000-0000-000000000000" Then
                    DeliverableCreate t.Name, t.Start, t.Finish, t.Guid
                    DeliverableUpdate t.DeliverableGuid, t.Name, t.Start, t.Finish
                End If
                If t.DeliverableGuid <> "00000000-0000-0000-0000-000000000000" Then
                    DeliverableDelete (t.DeliverableGuid)
                End If
            End If
        Next t
    End Sub

    This macro loops through all the tasks. If the flag field "Pub Deliverable" is set to yes, then it either creates or updated the deliverable. If it is set to no and there is a deliverable associated with the task, the deliverable is deleted.

    Before you can run this code, you will need to publish and create a workspace for your project. To run it, follow these steps:

    1. Open your Project Plan

    2. Press Alt+F11 – This will open the VBA Editor

    3. Double click on ThisProject Object:

    4. Copy the VBA code into the Object

    5. Run it by clicking on the Play button:

    With this solution, the user can simply flag each task that they want published as a deliverable and run the macro. If you want to have this code executed regularly without user intervention, you might want to consider placing this code in an event handler (VBA Event Handler Example).

    Chris Boyd

  • Project Programmability and Business Intelligence

    Checking Errors

    Here is a great recommendation from Jim Corbin:

    There are two main places to check for configuration and runtime errors when you are developing solutions for Project Server. The Unified Logging Service (ULS) trace logs can be more detailed than the application event log.

    · Application events   In the Start menu on the Project Server computer, click Run, and then type eventvwr. In the left pane of the Event Viewer window, click Application to see the events logged by Project Server, Windows SharePoint Services, ASP.NET, SQL Server, custom event handlers, and other applications. The Project Server event sources include ProjectQueueService and pjevtsvc.

    · ULS You can configure the ULS trace log to record specific or all categories and levels of activities in Project Server and SharePoint. To view the trace logs, you can use Windows Notepad, Microsoft Excel, or the Log Viewer add-in feature for SharePoint. Log Viewer is a useful download that is available from CodePlex.

    To configure the ULS trace log for a specific Project category:

    1. Open the SharePoint 3.0 Central Administration application, click Operations, and then click Diagnostic logging in the Logging and Reporting section.

    2. In the Event Throttling section, select a specific category such as Project Server - Server-Side Events. If you select Project Server - General, all Project categories will be logged.

    3. Select the Verbose level for the least critical event to report in the trace log.

    Caution   Run verbose logging only when you need it. Especially on a production server, select only a specific category. The size of logs can grow to be large. To turn off all logging, select the empty category and None for the trace log least critical event. To record relatively few events, select High or Monitorable.

    4. Use the default path for the trace logs, for example, C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\LOGS\. If you use the Log Viewer add-in for SharePoint, it looks for trace logs in the default path.

    5. The default maximum number of log files to maintain is 96.

    6. The default number of minutes to use a log file is 30. That is, ULS tracing creates a new log file after 30 minutes. Log files include the date and time in the filename, for example, SERVERNAME -20070424-1612.log.

    To use the Log Viewer add-in for SharePoint:

    1. Download and install the Log Viewer feature from CodePlex SharePoint 2007 Features. See the Releases tab for the list of downloads. The release notes include installation instructions.

    2. The Log Viewer is a global feature. After it is installed, click Operations on the Central Administration page, and then click View Unified Logging Service in the Utilities section.

    3. On the Unified Logging Service (ULS) Logs page, select a file to view, and then select the Project Server category. That shows all of the specific categories such as Project Server Queue, Project Server Server-Side Events, Project Server Reporting, and so forth.

    4. To see all events in the ULS log, leave the trace severity drop-down list blank. If you select Verbose, that shows only the verbose level events.

    5. Click Go.

    Log Viewer add-in for SharePoint showing a ULS log for Project


  • Project Programmability and Business Intelligence

    Connecting a Project Task Pane App to PWA



    Apps for Office present a great new opportunity to bring data from a variety of sources into Office applications. The new Project specifically offers a very powerful way to surface both SharePoint and Project Web App data in a task pane app in Project Professional. To connect with SharePoint or PWA from the client, we use on-the-fly OAuth flow as presented in the SharePoint 2013 SDK. The App for Project described in this article connects with the same PWA site collection that Project Professional is currently connected to, and displays data about the currently selected task that is not otherwise available from within Project Professional. This will work with all versions of Project Professional (C2R, MSI, On Demand) and with all PWA environments (Project Online, Project Server).



    Prerequisites for this project are:

    • Visual Studio 2012
    • IIS or IIS Express configured to allow applications to run on your server (at localhost)
    • Office 2013 and SharePoint 2013 tools for Visual Studio, available here
    • Project Professional 2013
    • Project Online tenant or Project Server

    The first step is to launch Visual Studio 2012. Create a new App for Office 2013 project as shown below. Let’s call it “TaskLastModifiedApp”.


    In the next dialog, make this a Task Pane App for Project.

    We need to add references, as this app will use a number of APIs across Office and SharePoint. These DLLs may be in a different location on your system. Most of these references are automatically added if you use the App for SharePoint template, so if you can’t find these on your system, create a quick App for SharePoint solution and note the reference paths to help you out. You should add:

    • C:\Program Files\Reference Assemblies\Microsoft\Windows Identity Foundation\v3.5\Microsoft.IdentityModel.dll
    • C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.IdentityModel.Extensions\v4.0_2.0.0.0__69c3241e6f0468ca\Microsoft.IdentityModel.Extensions.dll
    • C:\Program Files\Reference Assemblies\Microsoft\Windows Identity Foundation\v3.5\Microsoft.IdentityModel.WindowsTokenService.dll
    • C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.ProjectServer.Client.dll
    • C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.dll
    • C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\ISAPI\Microsoft.SharePoint.Client.Runtime.dll
    • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.IdentityModel.dll
    • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.IdentityModel.Selectors.dll
    • C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.ServiceModel.dll

    Additionally, you will need to add a copy of TokenHelper.cs, which is generated when creating an autohosted or provider-hosted App for SharePoint project.

    Task Pane App Manifest

    The actual task pane app is just an XML manifest. Open up TaskLastModifiedApp.xml from the TaskLastModifiedApp project in your Solution Explorer. Replace its contents with the following:

     1: <?xml version="1.0" encoding="UTF-8"?>
     2: <OfficeApp xmlns="" xmlns:xsi="" xsi:type="TaskPaneApp">
     3:   <Id>[leave this line alone]</Id>
     4:   <Version>1.0</Version>
     5:   <ProviderName>Microsoft</ProviderName>
     6:   <DefaultLocale>en-US</DefaultLocale>
     7:   <DisplayName DefaultValue="TaskLastModifiedApp" />
     8:   <Description DefaultValue="This app will show you when the selected task was last modified"/>
     9:   <AppDomains>
     10:     <AppDomain>https://localhost:44301/</AppDomain>
     11:   </AppDomains>
     12:   <Capabilities>
     13:     <Capability Name="Project" />
     14:   </Capabilities>
     15:   <DefaultSettings>
     16:     <SourceLocation DefaultValue="https://localhost:44301/pages/URLConstructor.aspx" />
     17:   </DefaultSettings>
     18:   <Permissions>ReadWriteDocument</Permissions>
     19: </OfficeApp>

    Replace the port after localhost (in both instances) with whatever port you have configured IIS to use for SSL. Make sure to toggle the “SSL Enabled” property on the TaskLastModifiedAppWeb project to true. Let whatever ID was originally set in the manifest remain.


    Next, delete the TaskLastModifiedApp.html page – we will need .aspx pages in this project. The basic architecture of the task pane app is as follows:

    • When the task pane app is launched, it loads “URLConstructor.aspx”, which pulls the PWA URL from the client and constructs a call to OAuthAuthorize with the proper tokens to request permissions for the app to access PWA data. This page loads “URLConstructor.js” to interact with the client.
    • OAuthAuthorize is launched in a new window, since we cannot predict the customer’s Project domain. After the user trusts the app, that new window is redirected to “PostOAuth.aspx”, which surfaces the auth code back to URLConstructor.aspx.
    • Once URLConstructor.aspx has the auth code, the task pane app is redirected with this as a token to “Default.aspx”, which has the functional code for the app. This page uses Project CSOM code in its code-behind page to read data from PWA, as well as “TaskLastModifiedApp.js” to interact with the client.

    Constructing the OAuthAuthorize URL

    The complete code for URLConstructor.aspx is as follows:

     1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="URLConstructor.aspx.cs" Inherits="TaskLastModifiedAppWeb.Pages.URLConstructor" %>
     3: <!DOCTYPE html>
     5: <html xmlns="">
     7: <head runat="server">
     8:     <title>TaskLastModifiedApp</title>
     9:     <meta http-equiv="X-UA-Compatible" content="IE=9" />
     10:     <script type="text/javascript" src="..\Scripts\Office\1.0\MicrosoftAjax.js"></script>
     11:     <script type="text/javascript" src="..\Scripts\Office\1.0\Office.js"></script>
     12:     <script type="text/javascript" src="..\Scripts\URLConstructor.js"></script>
     13:     <script type="text/javascript">
     14:         function getClientId() {
     15:             var clientId = '<%=ConfigurationManager.AppSettings["ClientId"].ToString() %>'; //read the clientID from web.config
     16:             getPwaUrl(clientId); //return to client code
     17:         }</script>
     18: </head>
     20: <body>
     21:     Redirecting...
     22: </body>
     24: </html>

    This page needs to be an .aspx page in order to read from web.config, but does not need anything in its code-behind. The clientId read from web.config is needed for the authorization flow. getPwaUrl() is a function within URLConstructor.js.

    The complete code for URLConstructor.js is as follows:

     1: var _projDoc;
     2: var pwaUrl;
     3: var oAuthUrl;
     5: Office.initialize = function (reason) {
     6:     _projDoc = Office.context.document;
     7:     getClientId(); //on document load, reads the ClientId from web.config first since it is server-side
     8: }
     10: function getPwaUrl(clientId) { //uses Office App API to read PWA URL
     11:     _projDoc.getProjectFieldAsync(Office.ProjectProjectFields.ProjectServerUrl,
     12:         function (asyncResult) {
     13:             if (asyncResult.status == Office.AsyncResultStatus.Succeeded) {
     14:                 pwaUrl = asyncResult.value.fieldValue;
     15:                 generateUrl(clientId); //creates the OAuthAuthorize URL with necessary parameters
     16:             }
     17:             else {
     18:                 logMethodError("getProjectFieldAsync",, asyncResult.error.message);
     19:             }
     20:         }
     21:     )
     22: };
     24: function generateUrl(clientId) {
     25:     oAuthUrl = pwaUrl + "/_layouts/15/OAuthAuthorize.aspx?IsDlg=1&client_id=" + clientId + "&scope=Projects.Read&response_type=code&redirect_uri=https://localhost:44301/pages/PostOAuth.aspx";
     26:     authWindow =;
     27:     codeListener(); //start listening for the auth code
     28: }
     30: function codeListener() {
     31:     setTimeout(function () { readCode(); }, 1000); //check for the auth code every one second
     32: }
     34: function readCode() {
     35:     try { //if we can actually reach the authCode field on PostOAuth.aspx
     36:         authCode = authWindow.document.getElementById("authCode").value;  //pull the authCode value
     37:         if (authCode != "NA") { //if it is not the default "NA"
     38:             authWindow.close(); //close the new window
     39:             document.location.href = "/Pages/Default.aspx?code=" + authCode; //redirect task pane to the app code with the authCode token
     40:         }
     41:     }
     42:     catch (e) {
     43:         codeListener(); //if we couldn't reach PostOAuth.aspx, wait another second and try again
     44:     }
     45: }

    When the task pane app loads, it first reads web.config from the aspx page, since this is server-side code. Once it has the clientId, we read the PWA URL. We then create the full OAuthAuthorize URL with the parameters specified above. scope=Projects.Read requests read permission to projects on the current PWA site. Make sure to match the SSL port here as well, as before.

    On running the app, a new window will open up outside of Project that prompts the user to login to PWA (if they have not checked “Keep me signed in” previously). They will then be presented with a “Do you trust…” page, the same as if they were installing an App for SharePoint. This is the OAuthAuthorize.aspx page. Once trusted, that new window navigates to PostOAuth.aspx, presented below:

     1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="PostOAuth.aspx.cs" Inherits="TaskLastModifiedAppWeb.Pages.PostOAuth" %>
     3: <!DOCTYPE html>
     5: <html xmlns="">
     6: <head runat="server">
     7:     <title></title>
     8: </head>
     9: <body>
     10:     <form id="form1" runat="server">
     11:     <div>
     12:     <asp:HiddenField ID="authCode" runat="server" value="NA"/>
     13:         Closing...
     14:     </div>
     15:     </form>
     16: </body>
     17: </html>

    And PostOAuth.aspx.cs:

     1: using System;
     2: using System.Collections.Generic;
     3: using System.Linq;
     4: using System.Web;
     5: using System.Web.UI;
     6: using System.Web.UI.WebControls;
     8: namespace TaskLastModifiedAppWeb.Pages
     9: {
     10:     public partial class PostOAuth : System.Web.UI.Page
     11:     {
     12:         protected void Page_Load(object sender, EventArgs e)
     13:         {
     14:             var code = Request.QueryString["code"];
     15:             authCode.Value = code;
     16:         }
     17:     }
     18: }

    This page saves the auth code token in a hidden field. The task pane app, still on URLConstructor.aspx, waits for this value and then closes the new window. The app then continues on to default.aspx with the proper code token needed to finish the OAuth flow.

    Reading the Last Modified Date of the Selected Task

    The remainder of this article is an example of what you might do in your task pane app now that you have read access to PWA data. This example will show you the Last Modified date and time of the task you have selected. On launch, it shows you data for the selected task, and as you change tasks, the data is updated using an event handler.

    The complete code for Default.aspx is as follows:

     1: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TaskLastModifiedAppWeb.Pages.Default" %>
     3: <!DOCTYPE html>
     5: <html xmlns="">
     6: <head runat="server">
     7:     <title>TaskLastModifiedApp</title>
     8:     <meta http-equiv="X-UA-Compatible" content="IE=9" />
     9:     <script type="text/javascript" src="..\Scripts\Office\1.0\MicrosoftAjax.js"></script>
     10:     <script type="text/javascript" src="..\Scripts\Office\1.0\Office.js"></script>
     11:     <script type="text/javascript" src="..\Scripts\TaskLastModifiedApp.js"></script>
     13: </head>
     15: <body>
     16:     <form id="form1" runat="server">
     18:         <asp:HiddenField ID="projGuidHF" runat="server" />
     19:         <asp:HiddenField ID="taskGuidHF" runat="server" />
     20:         <asp:HiddenField ID="pwaUrlHF" runat="server" />
     22:         <asp:Label ID="TaskLastModLabel" runat="server" Text="Loading..." />
     24:         <div style="display: none;">
     25:             <asp:Button ID="hiddenTaskChangedButton" runat="server" OnClick="OnTaskChanged" />
     26:         </div>
     28:     </form>
     30: </body>
     31: </html>

    This page contains three hidden fields used to pass data back and forth between the client-side code and the server-side code. It also leverages a label to surface the results to the user, and a hidden button that the client-side code uses to call a server-side function, as you will see below.

    The complete code for TaskLastModifiedApp.js is as follows:

     1: var _projDoc;
     2: var taskGuid;
     3: var projGuid;
     4: var pwaUrl;
     6: // This runs after every postback
     7: Office.initialize = function (reason) {
     8:     _projDoc = Office.context.document;
     9:     if (document.getElementById("pwaUrlHF").value == "NA") { //if this is the first run
     10:         firstRun();
     11:     }
     12:     manageTaskEventHandler('addHandlerAsync'); //need to re-register event handler after each postback
     13: }
     15: // Only need these on the first page load, not on postbacks
     16: function firstRun() {
     17:     getProjGuid();
     18:     _projDoc.getProjectFieldAsync(Office.ProjectProjectFields.ProjectServerUrl,
     19:         function (asyncResult) {
     20:             pwaUrl = asyncResult.value.fieldValue;
     21:             document.getElementById("pwaUrlHF").value = pwaUrl;
     22:         }
     23:     )
     24:     getTaskGuid();
     25: }
     27: // Get the GUID of the selected task, comes from SDK
     28: function getTaskGuid() {
     29:     var TaskLastModLabel = document.getElementById("TaskLastModLabel");
     30:     TaskLastModLabel.innerHTML = "Loading...";
     31:     _projDoc.getSelectedTaskAsync(function (asyncResult) {
     32:         taskGuid = asyncResult.value;
     33:         document.getElementById("taskGuidHF").value = taskGuid; //saves the task GUID to a hidden field to pass to the code-behind
     34:         document.getElementById("hiddenTaskChangedButton").click(); //runs the CSOM calls in the aspx.cs file
     35:     });
     36: }
     38:  // Get the GUID of the current project.
     39: function getProjGuid() {
     40:     _projDoc.getProjectFieldAsync(Office.ProjectProjectFields.GUID,
     41:         function (asyncResult) {
     42:             projGuid = asyncResult.value.fieldValue;
     43:             document.getElementById("projGuidHF").value = projGuid; //saves the project GUID to a hidden field to pass to the code-behind
     44:         }
     45:     )
     46: }
     48: // Task selection changed event handler.
     49: function onTaskSelectionChanged(eventArgs) {
     50:     getTaskGuid();
     51: }
     53: // Add or remove a task selection changed event handler.
     54: function manageTaskEventHandler(docMethod) {
     55:     manageEventHandlerAsync(
     56:         Office.EventType.TaskSelectionChanged,      // The task selection changed event.
     57:         onTaskSelectionChanged,                     // The event handler.
     58:         docMethod                // The Office.Document method to add or remove an event handler.
     59:     );
     60: }
     62: // Add or remove the specified event handler.
     63: function manageEventHandlerAsync(eventType, handler, operation, onComplete) {
     64:     _projDoc[operation]   //The operation is addHandlerAsync or removeHandlerAsync.
     65:     (
     66:         eventType,
     67:         handler,
     68:         function (asyncResult) {
     69:             // code here runs after event has been registered (or failed)
     70:         }
     71:     );
     72: }

    The first time this code runs, it pulls the PWA URL (just like we did in URLConstructor.js) and saves it to one of our hidden fields, registers a client-side event handler to capture when a new task is selected, and starts the process of connecting to PWA and pulling the data we need, which is mostly done in the code-behind file. After each postback, we do not need to recapture the PWA URL, as our ProjectContext is maintained as a static variable.

    We need to make one quick tweak to TokenHelper.cs first – change GetRealmFromTargetUrl from a private method to a public method.

    The complete code for Default.aspx.cs is as follows:

     1: using System;
     2: using System.Collections.Generic;
     3: using System.Linq;
     4: using System.Web;
     5: using System.Web.UI;
     6: using System.Web.UI.WebControls;
     7: using Microsoft.ProjectServer.Client;
     8: using Microsoft.SharePoint.Client;
     10: namespace TaskLastModifiedAppWeb.Pages
     11: {
     12:     public partial class Default : System.Web.UI.Page
     13:     {
     14:         public static ProjectContext projContext;
     15:         public static PublishedProject thisProj;
     16:         public Guid projGuid;
     18:         protected void Page_Load(object sender, EventArgs e)
     19:         {
     20:             if (!IsPostBack) //set values of hidden fields if this is the first page load
     21:             {
     22:                 projGuidHF.Value = "NA";
     23:                 taskGuidHF.Value = "NA";
     24:                 pwaUrlHF.Value = "NA";
     25:             }
     26:         }
     29:         protected void GetContexts()
     30:         {
     31:             var code = Request.QueryString["code"]; //pulls the code token from the request
     33:             string targetPwa = pwaUrlHF.Value; //pulls the PWA URL from where the Office app API stored it
     35:             Uri targetPwaUri = new Uri(targetPwa);
     37:             var tRealm = TokenHelper.GetRealmFromTargetUrl(targetPwaUri);
     39:             Uri rUri = new Uri("https://localhost:44301/pages/PostOAuth.aspx"); //hardcoded link to redirect_uri
     41:             var clientContext = TokenHelper.GetClientContextWithAuthorizationCode(targetPwa, "00000003-0000-0ff1-ce00-000000000000", code, tRealm, rUri);
     43:             projContext = GetProjectContextWithAuthorizationCode(targetPwa, "00000003-0000-0ff1-ce00-000000000000", code, tRealm, rUri);
     45:             projGuid = new Guid("{" + projGuidHF.Value + "}"); //loads the current project through CSOM
     47:             var projects = projContext.LoadQuery(projContext.Projects.Where(proj => proj.Id == projGuid));
     48:             projContext.ExecuteQuery();
     49:             thisProj = projects.First();
     50:         }
     52:         protected void OnTaskChanged(object sender, EventArgs e) //determine the selected task's last modified date
     53:         {
     54:             if (thisProj == null)
     55:             {
     56:                 GetContexts();
     57:             }
     59:             var taskGuid = new Guid(taskGuidHF.Value);
     61:             var tasks = projContext.LoadQuery(thisProj.Tasks.Where(task => task.Id == taskGuid)); //load the selected task off of the project
     62:             projContext.ExecuteQuery();
     63:             PublishedTask thisTask = tasks.First();
     64:             string dateMod = thisTask.Modified.ToString("D"); //pull out the Modified field on the task
     65:             string timeMod = thisTask.Modified.ToString("t");
     66:             TaskLastModLabel.Text = "The selected task was last modified on " + dateMod + " at " + timeMod + ".";
     67:         }
     69:         public static ProjectContext GetProjectContextWithAuthorizationCode(string targetUrl,string targetPrincipalName,string authorizationCode,string targetRealm,Uri redirectUri)
     70:         {
     71:             Uri targetUri = new Uri(targetUrl);
     73:             string accessToken =
     74:                 TokenHelper.GetAccessToken(authorizationCode, targetPrincipalName, targetUri.Authority, targetRealm, redirectUri).AccessToken;
     76:             return GetProjectContextWithAccessToken(targetUrl, accessToken);
     77:         }
     79:         public static ProjectContext GetProjectContextWithAccessToken(string targetUrl, string accessToken)
     80:         {
     81:             Uri targetUri = new Uri(targetUrl);
     83:             ProjectContext projContext = new ProjectContext(targetUrl);
     85:             projContext.AuthenticationMode = ClientAuthenticationMode.Anonymous;
     86:             projContext.FormDigestHandlingEnabled = false;
     87:             projContext.ExecutingWebRequest +=
     88:                 delegate(object oSender, WebRequestEventArgs webRequestEventArgs)
     89:                 {
     90:                     webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =
     91:                         "Bearer " + accessToken;
     92:                 };
     94:             return projContext;
     95:         }
     96:     }
     97: }


    The first time the page loads, we need to initialize the hidden field values. This enables us to not set them directly in Default.aspx (and thus lose their values after a postback) and lets the client-side code distinguish between a first run load and a postback.


    This code also only runs once, assuming the current project remains loaded. This handles the last part of the OAuth flow – we use the code token from OAuthAuthorize to generate a client context and a project context using methods in TokenHelper.cs, as well as slightly modified methods GetProjectContextWithAuthorizationCode() and GetProjectContextWithAccessToken().


    This first checks to make sure we have a project loaded from which to pull data. We then read the selected task guid from the hidden field, which was updated client-side before this method was called. We use Project CSOM to load the selected task from PWA and read its Last Modified field, which is then presented to the user in a readable format using the label on Default.aspx.

    Register the App ID and App Secret

    Since we are not submitting this app to the Office Store, we need to register it on our test tenant. On your tenant, navigate to https://[your PWA site]/_layouts/15/appregnew.aspx. Generate a new App Id and App Secret, set the Title to “TaskLastModifiedApp”, set the App Domain to localhost:44301 (or wherever the app code is running), and set the Redirect URI to match the redirect_uri token value in the oAuth URL created in URLConstructor.js.


    Hit Create, and then add the App Id and App Secret to web.config in your Visual Studio solution. It should look like the following when you are done, with your values for ClientId and ClientSecret:

     1: <?xml version="1.0"?>
     3: <configuration>
     4:     <system.web>
     5:       <compilation debug="true" targetFramework="4.0" />
     6:     </system.web>
     7:   <appSettings>
     8:     <add key="ClientId" value="a9ce3d5a-bb14-4aad-9c27-41a05c473b4d" />
     9:     <add key="ClientSecret" value="hL0C8wt2PPaBYNYRMZzcUcu3C/Vv0fbm48djGzyIXOw=" />
     10:   </appSettings>
     11: </configuration>

    Time to Test!

    To run the app, just hit F5 in Visual Studio, which will launch Project Professional. Make sure to connect to a PWA profile, then load a published project (or create and publish a new one). From a task view, select the Project tab in the ribbon, hit the dropdown for Apps for Office, and click TaskLastModifiedApp.


    The app will launch in a task pane on the right side of the screen. It’ll prompt you to trust the app, quickly load, and then display the last modified date and time of the selected task.


    Select a different task, and the data will update almost instantly.



    In this blog post, you have learned how to create a task pane app in Project Professional that can display data from PWA that would not normally be visible in the client. The app uses SharePoint’s OAuthAuthorize page to request permissions from PWA and handle the authentication handshake between the app code and your online data. For more on working with OAuth, make sure to check out the relevant node in the SharePoint 2013 SDK. For more information on PWA programmability, check out the Project 2013 SDK. To learn more about writing task pane apps for the new Project, see this node in the Office 2013 SDK.

  • Project Programmability and Business Intelligence

    Getting at the Task Time Phased Data


    I have been asked many times how to get the task time phased data from the PSI. Unfortunately there is no way to get this through the PSI. I suggest that you either go to the RDB for this information or the cubes. Here is a quick example.

    I created a project with one task and broke the work down over a week:


    I saved and published the project so that it would make its way into the reporting database. Here is the query I wrote to retrieve this data:

        INNER JOIN MSP_EpmTask_UserView 
    ON MSP_EpmAssignmentByDay_UserView.TaskUID = MSP_EpmTask_UserView.TaskUID WHERE (MSP_EpmTask_UserView.TaskName = 'Task 1')

    Here is the result of the query:

    Chris Boyd

  • Project Programmability and Business Intelligence

    ANNOUNCING: Microsoft Project 2010 Demonstration and Evaluation Installation Pack for RTM is HERE!


    This is a great news! We have just released the Microsoft Project 2010 Demonstration and Evaluation Installation Pack that builds on top of 2010 Information Worker Demonstration and Evaluation Virtual Machine  that SharePoint team has released recently. And yes the Virtual Image that has to be downloaded, imported and running prior to installing “our” Microsoft Project 2010 Demonstration and Evaluation Installation Pack.

    We also advice to watch the following webcast: Project 2010 Virtual Image, Getting Started Quickly (Level 100) 


    - End to end project lifecycle


    - New Business Intelligence story!


    - Built on top of common 2010 Information Worker Demonstration and Evaluation Virtual Machine  using NEW Project 2010 Demo Installation Platform


    Demo storyline

    It follows the life of a project from inception through to completion! Demo storyline offers a comprehensive overview of the new and existing capabilities in:

    · Microsoft Project Professional 2010

    · Microsoft Project Server 2010

    · Microsoft SharePoint 2010 products and technologies

    · Microsoft Office 2010 Products

    More information about the Project 2010 Demo Solution in this comprehensive XPS document (also available on the download page

    Project 2010 Demo Installation Platform

    We have published the entire source code of the Project 2010 Demo Installation Platform so us and partners could extend on it and built future demo packs for Project 2010!


    Q: Do I need to download, import and run the 2010 Information Worker Demonstration and Evaluation Virtual Machine ( before installing the Microsoft Project 2010 Demonstration and Evaluation Installation Pack?

    A: Absolutely yes. Refer to this comprehensive XPS document for more details.

    Q: Could I install this Microsoft Project 2010 Demonstration and Evaluation Installation Pack on any other VM, including Project 2010 Beta?

    A: No.

    Q: Is there anything around evaluation period and expiration I need to pay attention to?

    A: Please follow information included with 2010 Information Worker Demonstration and Evaluation Virtual Machine to properly understand procedure like re-arming and activation so you can prevent lot of unpleasant surprises!!!

    Q: Any recommended hardware for good performance?

    A: Yes -  we specify the additional HW requirements in this comprehensive XPS document. We recommend Solid State Drive for great performance. More about HW we use for demoing here.

    Q: How long does it take to install the Microsoft Project 2010 Demonstration and Evaluation Installation Pack on top of 2010 Information Worker Demonstration and Evaluation Virtual Machine?

    A: If the above VM is already running and working properly it takes up to 30 minutes. If you are using Solid State Drive it’s usually around 10 minutes.

    Q: What is in the Microsoft Project 2010 Demonstration and Evaluation Installation Pack?

    A: We have packaged the following content: 4 core Project databases, 1 SharePoint Content database, 1 OLAP Database, number of WSP customizations (check the Solution Starter webcast series here if you want to learn more), additional demo files and PowerShell script for configuring and deploying the whole solution. So that’s it in a nutshell. Check this link for more details

    Q: Could I only extract the Microsoft Project 2010 Demonstration and Evaluation Installation Pack?

    A: Yes, we have included that option as well based on the demand, but it’s intended for expert use only.

    Q: What if I have additional questions about this Project 2010 Demo Solution?

    A: You have several options – please check this webcast and search or post your questions in this forum.

  • Project Programmability and Business Intelligence

    Writing a PSI Extension for Project Server 2010


    The blog articles How to make PSI Extensions in Project Server 2010 - Part I and  Part II show how to create and use a simple PSI extension that reads a list of timesheets for the current user, by calling both the GetCurrentUserUid method and the ReadTimesheetList method in the extension. The code in the articles requires using the undocumented Microsoft.Office.Project.Server.Interfaces.dll and Microsoft.Office.Project.Server.Schema.dll assemblies, creates WCF channels for the Resource and Timesheet services, and then returns an XML string of the results.

    The blog articles show more information about using WCF development in Project Server than the Project 2010 SDK includes. For background information about why PSI extensions in Project Server 2010 are different than extensions in Project Server 2007, read those articles.

    The PSI Extensions in Project Server 2010.docx article in the attachment ( shows how to create and use a PSI extension that makes the same calls as in the previous blog, but with the following differences:

    • Use the Resource and Timesheet proxy service files, instead of the undocumented assemblies.
    • Configure the services programmatically, similar to the same way shown in Walkthrough: Developing PSI Applications Using WCF, instead of creating a WCF ChannelFactory.
    • Return a TimesheetListDataSet, instead of a string.
    • Use an app.config file for the client, as with any other PSI service, instead of creating a ChannelFactory.

    The attachment includes the draft article, complete code of the extension and a test application, and examples of the PSIExtensions.svc and web.config files for the PSI.


  • Project Programmability and Business Intelligence

    Reading Assignment Enterprise Custom Field Values with VBA



    There has been a number of people asking how to read assignment enterprise custom field values with VBA. In fact, we ran into this issue internally with our dogfood efforts and fixed it in SP1. So, if you need to get/set assignment custom field values, the first step is to download SP1:

    Once you have SP1 installed, it is fairly easy to read assignment enterprise custom fields. When you read and set task and resource enterprise custom fields, you use the GetField and SetField methods in VBA. To read and set the assignment values you don't use the GetField and SetField, but instead use the name of the enterprise custom field as a property of the assignment.  There are a couple of caveats, however:

    • The field name can't contain spaces in the name
    • When you're writing your code, you won't get auto complete to show you the field name. This is because the property isn't a part of the type library and therefore isn't early bound.  As long as you pass in a valid field name, however, then the code will late bind to it. 

    Here is a short example. Suppose your custom Field name is "ecfName", here is how you would read it:

    For Each T in ActiveProject.Tasks

      If Not (T is Nothing) Then

        For Each A in T.Assignments

          assignCFVal = A.ecfName

        Next A

      End If

    Next T

    Chris Boyd

  • Project Programmability and Business Intelligence

    Publishing Projects based off a Project Custom Field Value



    As part of the development process for the next version of Project, we are heavily relying on Project to manage all the work that is happening across team. As part of the process, we require up to date status reports. All the work is spread across 40 project plans, so we cannot rely on PMs publishing projects daily. To ensure the status reports are up to date, we publish all the projects nightly by running a custom application as a scheduled Windows task:



    Since we only want to publish our team's projects and cannot publish other teams projects on the server, we tag all of our team projects with text custom fields that have associated lookup table to ensure that everyone on the team is using the same tags:


    To setup the custom field and lookup tables:

    1. Log onto Project Server as an Administrator
    2. Click on "Server Settings" then click on "Enterprise Custom Field Definition"
    3. Scroll to the bottom of the page and click "New Lookup Table". We created a lookup table called "Office Division", which has all the divisions within Office:

    4. Next, create a custom field, which we called "Office Division", and associate it to the lookup table:


    The application to publish the projects nightly is attached to this post. It is a fairly simple application. At the top of the program class, we have set a few constants. If you wanted to try out this application, you should just have to change these constants and compile the application. These constraints are self explanatory:

    const string ls_projURL = "http://Office/PWA/";     // Server URL
    const string ls_CustomField = "Office Division";    // Custom Field 
    const string ls_LookupTableValue = "Project";       // Value in the lookup table

    Once we had the application built, we simple set up a scheduled task in Windows to run the application nightly:


    Chris Boyd

  • Project Programmability and Business Intelligence

    Update of Project 2010 SDK–Online and Download


    The Project 2010 SDK download and the MSDN online release are both updated. The updates have the same URLs as previous releases:

    New conceptual / how-to topic:

    ·Topic updates for MSDN online, since the last update on March 7, 2011:
    20 conceptual and how-to topics have updates. Most changes are relatively minor; the Change History table at the bottom of each topic shows significant changes.

    • Assn Element
    • ChangeList Elements
    • ChangeList Schema Reference
    • Developing Project Server Workflows
    • How to: Create a Project Server Event Handler and Log an Event
    • How to: Create a Proxy Assembly for WCF Services (SP1 note)
    • How to: Modify the Ribbon in PWA
    • Introduction to the ChangeList Schema and Statusing ChangeXML
    • Introduction to the SetAssignmentWorkData Schema
    • Prerequisites for ASMX-Based Code Samples (SP1 note)
    • Prerequisites for WCF-Based Code Samples (SP1 note)
    • Project 2010 SDK Documentation (overview of the Project 2010 SDK)
    • Project Server 2010 Programming Tasks
    • Project Server Error Codes
    • SetAssignmentWorkData Elements
    • SetAssignmentWorkData Schema Reference
    • Supported Project Fields and Field Information for Statusing ChangeXML (updated the valid change types for the Actual Overtime Work and Remaining Overtime Work fields)
    • Tables of VBA Object Model Changes
    • Walkthrough: Developing PSI Applications Using WCF
    • What's New for Developers in Project 2010 (programmability changes for SP1)

    600 managed code types (classes that include new descriptions for one or more properties, methods, and events) are updated. There are new code samples for the following PSI methods:

    • QueueDeleteProjects
    • ReadResource
    • UpdateStatus
    • ReadProjectStatus
    • SubmitStatusForResource
    • ReadStatusForResource
    • ReadEventHandlerAssociationsForEvent
    • UpdateEventHandlerAssociations
    • CreateEventHandlerAssociations

    New / updated items in the Project 2010 SDK download, which was last updated March 7:

    • Project2010SDK.chm is an HTML Help file that includes the same updated content that is online. In the managed code reference section, 99.8% of the types and members now have descriptions (26,530 out of a total 26,576 topics). That is up from 77.7% at RTM.
    • WINPROJ.DEV.hxs remains unchanged from the March update of VBA Help. There are instructions for replacing the local VBA Help file that was shipped with the Project RTM release.
    • IntelliSense files are updated for the PSI proxy assembly and the Project Server assemblies, to show descriptions of classes and members while programming in Visual Studio. The type and member descriptions have the same updates as in the HTML Help file (and in MSDN online).
    • The Microsoft.Office.Project.Server.Library.dll assembly is updated for distribution with third-party solutions for SP1.
    • Event handler solution: TestProjectEventHandlers.
    • Test application for the Queue System: UsingQueueSystem shows an example of when to wait for the queue, and when you don’t need to wait.
    • ProjTool has a minor update, so the functionality of the Project Details dialog matches the description in Using the ProjTool Test Application.
    • New PSI code samples include complete WCF-based solutions for the following events: ProjectEventReceiver: OnCreating, OnCreated, OnSaved. Other new solutions show the use of the following PSI methods: QueueDeleteProjects, CreateEventHandlerAssociations, UpdateEventHandlerAssociations, ReadEventHandlerAssociationsForEvent, using the ReadResources and ReadResource methods to get the RBS custom field, creating a changeXml parameter for a different resource with the UpdateStatus method, and using SubmitStatusForResource and ReadStatusForResource.
  • Project Programmability and Business Intelligence

    Creating Deliverable Reports


    I have had many questions with regards to the Deliverables feature and reporting. If your not familiar with Deliverables, I suggest you take a look at my blog post on the Project blog:

    Just like all other project data, when a project plan is published, the data makes it way to the reporting database. This allows you to create some very useful reports on deliverables and dependencies. To get started with reporting, you may want to read through this post:

    In this post, I am only going to provide some background information on Deliverables and a couple of queries to get you started with creating your own reports. To begin with, these are the views and tables that are most commonly used for Deliverable reports:


    This view shows all the projects. Commonly you will join the Project UID in this view with the Project UID or the Relationship UID from the other views. By doing this you can get information about the project the deliverable or dependency is associated with, such as the name of the project.

    This view lists all the published deliverables, not the dependencies. In this view you can get information such the UID for the project a deliverable is associated with and the start and finish date of a deliverable.

    This is the same at the MSP_WssDeliverableToProjectLinks_UserView except that is has additional fields for deliverables that are linked to tasks. This allows you to report on task details for the associated deliverable. For example, you could use the task information to write a report that shows all deliverables where the deliverable finish date is before the task finish date.

    This view shows all the different associates with risks, issues and deliverables. Here you are going to want to look at the relationship type ID. The relationship type ID tells you if it is a deliverable or a dependency and if it is linked to a task or not. It is also where you can find if a dependency exists.

    This table lists the different types. These types refer to risks, issues and deliverables. For deliverables and dependencies, the following types are important:

    Relationship Type ID


    11 This is a deliverable that is linked to a task.

    This is a dependency on a deliverable that is linked to a task. 


    This is a deliverable for a project. It is not linked to any task within the project.


    This is a dependency on a deliverable for a project. It is not linked to any task within the project.

    There are a set of common queries that user tend to want when creating a report for deliverables. This first query is a simple list of all the deliverables and what project they are associated with:

    	ProjectName As 'Project Name', 
    	Title As 'Deliverable', 
    	StartDate As 'Start Date', 
    	FinishDate As ' Finish Date'
    	Inner Join MSP_EpmProject_UserView 
          	On 	MSP_WssDeliverableToProjectLinks_UserView.ProjectUID =

    The following query lists all the projects that have taken dependencies on a deliverable for given project. For the query to work, you need to set ProjectSelect.

    	DeliverableProj.ProjectName AS SelectedProject,
    DependancyProj.ProjectName AS DependentProject,
    DeliverableLinks.Title, DeliverableLinks.StartDate, DeliverableLinks.FinishDate FROM MSP_EpmProject_UserView AS DeliverableProj INNER JOIN MSP_WssListItemAssociation ON DeliverableProj.ProjectUID =
    MSP_WssListItemAssociation.ProjectUID INNER JOIN MSP_EpmProject_UserView AS DependancyProj ON MSP_WssListItemAssociation.RelatedProjectUID =
    DependancyProj.ProjectUID INNER JOIN MSP_WssDeliverable AS DeliverableLinks ON MSP_WssListItemAssociation.ListItemUID = DeliverableLinks.DeliverableUniqueID WHERE (MSP_WssListItemAssociation.ProjectUID
    <> MSP_WssListItemAssociation.RelatedProjectUID) AND (DeliverableProj.ProjectName = @ProjectSelect)

    This last query lists all the projects that a given project is dependent on. Again, you need to set ProjectSelect for the query to work.

    	DependancyProj.ProjectName AS SelectedProject,
    DeliverableProj.ProjectName, DeliverableLinks.Title, DeliverableLinks.StartDate, DeliverableLinks.FinishDate FROM MSP_WssListItemAssociation INNER JOIN MSP_EpmProject_UserView AS DependancyProj ON MSP_WssListItemAssociation.RelatedProjectUID = DependancyProj.ProjectUID INNER JOIN MSP_EpmProject_UserView AS DeliverableProj ON MSP_WssListItemAssociation.ProjectUID = DeliverableProj.ProjectUID INNER JOIN MSP_WssDeliverable AS DeliverableLinks ON MSP_WssListItemAssociation.ListItemUID = DeliverableLinks.DeliverableUniqueID WHERE (MSP_WssListItemAssociation.RelatedProjectUID <> MSP_WssListItemAssociation.ProjectUID) AND (DependancyProj.ProjectName = @ProjectSelect)

    To take a look at the last two queries in real reports, check out the Project Give and Get Reports in the Report Pack:

    This should be a good start with creating Deliverable reports. If you come up with some interesting queries for creating Deliverable reports, please share them by posting them as comments!

    Chris Boyd



  • Project Programmability and Business Intelligence

    How to use the “Skip to Stage” Feature in Project Server 2010 Workflows



    The skip to stage feature is designed to allow administrators the ability to restart a workflow and have it attempt to skip along the workflow until it reaches a particular stage. A common scenario for this functionality is when a project has progressed to a specific point in a workflow, but needs to be pushed back to a previous stage due to any number of reasons, such as, a new stage was inserted that needs to be addressed, certain fields need to be changed that are exposed only in previous stages, etc.

    Project Server workflows, like SharePoint workflows, must always be executed linearly. They cannot begin execution at random locations within a workflow. Neither can they “jump” from one point to another point, unless coded to do so within the workflow. The skip to stage functionality cannot circumvent this limitation. As such, the issue with restarting a workflow and attempting to push it to a particular point is that any activities which “stopped” the workflow before will continue to do so again. For Example, activities such as SetProjectStage, OnProjectSubmit, OnProjectCommit, the officeTask, ect, will still cause the workflow to dehydrate and give control back to the user. Although for the SetProjectStage activity we have done some extra work and put logic inside it so that it can be skipped, so long as there are no required fields left in the stage

    Therefore, to fully work with the skip to stage functionality you will need to wrap activities within “if statements”. When an administrator restarts a workflow and chooses to skip to a certain stage, we pass into the workflow two properties. The first property indicates that a skip stage command has been sent, and the second property contains the stage Guid that the workflow should stop at. Our SetProjectStage activity reads these two variables and skips if the first variable is set to true, and if the current stage is not the stage that is set in the second property. However, it won’t skip if there are required fields in the stage. So you will need to mimic this behavior around your other activities. You should wrap all of the activities between the SetProjectStage activities (not including the SetProjectStage activity) with an ifElseActivity statement that checks the first Property.

    Customizing Workflows in Visual Studio to work with skip to stage

    To make any workflow work correctly with the skip to stage functionality an “if statement” should be placed around all of the activities between each of the SetProjectStages. The key example of when this would be necessary is for the office task activities.

    Screen shot of example workflow:


    In the above example the “ifElseActivity1” is used to bypass the leadApproval sequence activity, which contains all of our approval activities.

    As you can see from the above screen shot, it is the “if statement” that we need to figure out how to code. In order to do so, we need to be aware of the two properties that are passed into the workflow during a skip to stage event:

    WorkflowContext.SkipToStage : Boolean : If true, a skip to stage functionality has been requested

    WorkflowContext.StageUid : Guid : Guid of the stage the workflow should skip to

    Steps for creating the workflow:

    1. Insert and set the properties of a SetProjectStage activity

    · You will need to set the “AlwaysWait” property to True in order for the Skip to Stage scenario to properly work.

    · If you do not set the Always wait property to true the workflow may not be able to skip to and stop at previous stages.


    2. Insert an IfElseActivity

    3. Have the activity check the “projectSequence1.WorkflowContext.SkipToStage” variable.

    · If variable is set to false, execute all activities

    · Else, bypass



    The WorkflowContext.StageUid is not needed for the “If Statement” but is there in case you wish to check what stage the user is trying to skip to.

    *The complete above example is found in the attached Visual Studio solution.*

    Administration: Initiating the Skip to Stage feature

    To attempt to force a project to skip to a particular stage, do the following within Project Server.

    1. Log into Project Server as an Administrator

    2. Go to Server Setting


    3. Click on “Change or Restart Workflows”


    4. In the top drop down, select the EPT that the project(s) that you would like to restart and skip to a stage belong to.

    5. Select the project(s) which you would like restart and skip to stage from the left panel and add them to the right panel.


    6. Check the “Restart current workflow for the selected projects” option


    a. Alternatively you can also select the “Associate projects with a new Enterprise Project Type:” option. If you select an Enterprise Project Type that has a workflow associated with it, you can then select which stage in that workflow the project(s) should attempt to skip to.

    7. Next select which stage the project(s) should attempt to skip to

    a. Skip until the current workflow stage

    i. Selecting this option will tell the projects to attempt to skip until they reach the stage that the project(s) are in currently

    b. Skip to a particular workflow stage & then selecting a particular stage

    i. Selecting this option will tell the projects to attempt to skip until they reach the stage that has been selected

    ii. If the selected stage does not exist, the workflow will stop at the very first opportunity.

    8. Press OK

    9. The project restart jobs will be sent to the queue, and the projects will attempt to restart and skip.

    Skip to Stage Example

    Expected behavior of the skip to stage can be summed up by the following example.

    · A workflow has 3 stages

    · All stages are marked as “AlwaysWait”

    · Project is on Stage 2

    · Stage 1’s required custom fields are filled out

    · Stage 2’s required custom fields are not filled out

    · Stage 3’s required custom fields are not filled out

    Administrator will go and:

    o Restart Skip to current stage

    § This will make the workflow restart and stop at stage 2

    o Restart Skip to stage 1 (trying to make the workflow stop on a stage where all of the require custom fields are filled out)

    § This will make the workflow stop at stage 1 and wait for submit

    o Restart Skip to stage 3 (trying to make it jump over stage 2 even though its required custom fields are not yet filled out)

    § Workflow will stop at stage 2

    § Workflow will not be able to skip stage2 if it has required fields.

    § If you really want to skip it, you need wrap the SetProjectStage as well in an if/else skip condition, which checks the SkipToStage variable and the StageUid variable.

    o Restart Skip to stage 5 (trying to make the workflow stop on a stage that does not exist)

    § Workflow will stop at stage 1. This is because of the combination of “Destination stage” doesn’t exist & current stage=”Always wait”.


    This feature has been designed and created to address the majority of our customer needs when it comes to being able to skip between stages. In this blog we reviewed there are two steps to use this feature. The first is to correctly develop your workflows to be able to skip over the activities that are between the SetProjectStage activities. This is done by wrapping the activities within an ifElse activity. And the second stage is to restart the workflows and select the stage the project should attempt to skip to. This is done with Project Server --> Server Settings --> Change or Restart Workflows.

    If you have any additional questions please feel free to post comments, and I will follow up accordingly.

  • Project Programmability and Business Intelligence

    Improve the Performance of Building the Project Server Data Analysis Cube


    Jack Li has passed along a great tip on how to improve the performance of building the Project Server Data Analysis Cube: 


    Recently we have worked with a big enterprise customer using Project Server. They ran into an issue where Project Server reporting component which uses Analysis Server ran very slow during cube population. Eventually, the issue was tracked down to a single query.  The query would take 17 hours to finish for 1 year of data. nHowever, if one applied force order to the query, it would finish within minutes.


    Customer had tight deadline and need a solution fast. It would be easy if one could just modify the query to add force order option.  Unfortunately, the query was generated by Analysis Service based on the cube definition. In other words, it couldn’t  be changed.


    Enter into plan guide for the  rescue. SQL Server 2005 gives you various means to influence the query plan for a query without adding hints to the query itself directly. You don’t need to change the query which may not be possible without changing the application.


    The simplest one is the use use sp_create_plan_guide stored procedure to add a query hint like force order etc.


    Here is exactly what we did for our customer:


    1)    We  used profiler trace to trace the exact query

    2)    We copied the exact query text and use sp_create_plan_guide to create the plan guide:


    EXEC sp_create_plan_guide N'guide_forceorder',

        N'<exact query>',




    N'OPTION (force order)'



    Steps are fairly easy yet it is tricky to implement. If you are not doing it correctly, SQL Server may not use the plan guide you just created.  Here are a few things you need to watch for:


    1)    ensure you have the exact text.  Even if you miss a space character, the plan won’t match.   To ensure you get exact text, you should launch  profiler trace while running the application to get the query.   See for more details.


    2)    There are two ways you can verify if SQL actually uses your plan guide. The first one obviously is that your query finishes faster.  Another way is that you will see PlanGuideDB word in the xml plan. So if you do set showplan_xml on and then run the query, you will get xml showplan. If you search the text, you will find PlanguideDB.  


    There are more advanced ways to use plan guide including parameterization or use plan. Please refer SQL Server 2005 books online for these advanced topics.



  • Project Programmability and Business Intelligence

    Creating a Task Hierarchy


    The following questions has been asked:

    "[...] is there a way to set up the task hierarchy within a single project?"

    and the answer is YES! To create a hierarchy within a single project, you have to set the outline level for the sub tasks. The below example creates two task, Summary Task and Sub Task. For the Summary Task, we do not set the outline level, we just create it as normal task. The Sub Task is where you set the outline level. In this example we set the outline level to 2:

    Connection conn = new Connection("http://chboyd01/pwa");

    WSProject.Project projWS = (WSProject.Project)conn.GetWebService(Connection.Project);

    Guid sessGuid = Guid.NewGuid();
    Guid jobGuid = Guid.NewGuid();
    Guid taskGuid = Guid.NewGuid();

    Guid projGuid = GetProjectUidFromProjectName("Excel");

    projWS.CheckOutProject(projGuid, sessGuid, "");

    WSProject.ProjectDataSet dsP;


    // Create a task with a constraint


    dsP = new WSProject.ProjectDataSet();

    WSProject.ProjectDataSet.TaskRow taskRow = dsP.Task.NewTaskRow();


    // Set the required fields

    taskRow.PROJ_UID = projGuid;
    taskRow.TASK_UID = taskGuid;
    taskRow.TASK_NAME =
    "Summary Task";

    taskRow.AddPosition = (int)PSLibrary.Task.AddPositionType.Last;


    projWS.QueueAddToProject(jobGuid, sessGuid, dsP, false);

    // Create a second task

    dsP = new WSProject.ProjectDataSet();


    taskRow = dsP.Task.NewTaskRow();

    Guid task2Guid = Guid.NewGuid();

    jobGuid = Guid.NewGuid();

    // Set the required fields

    taskRow.PROJ_UID = projGuid;
    taskRow.TASK_UID = task2Guid;
    taskRow.TASK_NAME =
    "Sub Task";

    // Set the start and finish dates

    taskRow.TASK_START_DATE = new DateTime(2007, 01, 31);
    taskRow.TASK_FINISH_DATE =
    new DateTime(2007, 02, 03);

    taskRow.TASK_OUTLINE_LEVEL = 2;

    taskRow.AddPosition = (int)PSLibrary.Task.AddPositionType.Last;


    projWS.QueueAddToProject(jobGuid, sessGuid, dsP, false);


    The below screen shot is the result of running the above sample code:

    Chris Boyd


    Technorati tags: , , ,
  • Project Programmability and Business Intelligence

    Technical Resources on Workflow/Demand Management in Project Server 2010



    This blog entry is designed to try and capture all of the different pieces of information that exists right now regarding workflows in Project Server 2010.  Based on the feedback we are in process of creating a dedicated “Demand Management” resource center on Microsoft TechNet that will became the “one-stop” shop for all your technical and developer resources for Demand Management, including Workflows in Project 2010.


    Demand Management in Project Server 2010


    Developing Project Server Workflows:

    Out of the Box Sample Workflow Source Code:

    Download the SDK: and find the source code in the “Samples\Workflow\SampleProposal2” directory

    Sharepoint Office Task Class:

    Using InfoPath Forms in a Workflow:


    Introduction to Demand Management:

    End User level Walkthrough of the Out of the Box Sample Workflow:

    How to use the Skip to Stage Feature:

    Debugging a Project Server Workflow in Visual Studio 2010

    Solution Starters (Open Source Solution Starters)

     Microsoft Project 2010 Solution Starters

    Recorded Videos:

    Demand Management Overview:

    Workflow Deep Dive:


    Demand Management Overview:

    Part 1:

    Part 2:

    Part 3:

    Part 4:

    Workflow Deep Dive:

    Part 1:

    Part 2:

    Tailored Tools for Workflow Creation:

    Project Forum:,projectprofessional2010/

    Project Server 2010 Demonstration Virtual Machine Image Download:

    Hitchhiker’s Guide to Demand Management (white paper)

  • Project Programmability and Business Intelligence

    Security in Project Server 2010–What about Custom Permissions?


    SharePoint Server 2010 handles user authentication through claims processing, which is a new feature for SharePoint and Project Server. SharePoint handles both Windows authentication and Forms authentication for Project Server users. For authorization, you can use the ReadResourceAuthorization and SetResourceAuthorization methods in the Resource service of the PSI. Because you probably don’t often change security authorization settings for users, you would normally go to the Manage Users page in Project Web App to select a user and set the global and category permissions.

    The Security business object in Project Server (with programmatic access through the PSI Security service) manages security groups, categories, templates, and the global Project Web App permissions. The Security service can add existing permissions or remove permissions from the sets available for Project Server users. However, the Security service does not have a method for creating a custom permission. For example, if you created a Project Server extension that updates a Siebel CRM system, you might want a custom permission that enabled users to use that extension.

    In Office Project Server 2007, you can create custom global and category permissions by modifying security tables in the Published database. Custom permissions show in the PWA lists of permissions, where Project Server administrators can secure the 3rd-party extension the same way they secure other Project Server features. The Walkthrough: Creating and Using Custom Project Server Permissions article is the only SDK example where an exception is made for changing the Published database. 

    NOTE:  The Project team would like some feedback on the importance of custom permissions. If you need to create custom permissions in Project Server, please respond to this post.

     In Project Server 2010, that process for creating custom permissions still works as it did in Project Server 2007. In future versions of Project Server, no modifications to tables in the Published, Draft, or Archive databases will be supported. Custom permissions and secure links that rely on table modifications still work in Project Server 2010, but that process will be deprecated. As an alternative, you may have to create your own user interface to manage custom permissions, or use claims augmentation in a custom application. For more information, see Claims Provider.


    For more information about Project Server security, including a discussion of global and category permissions, see the Project Server Security Primer in the Project Server 2007 SDK and Security and protection for Project Server 2010 in TechNet.

  • Project Programmability and Business Intelligence

    Project Server 2007 Timesheet Data Population Tool

    Technorati tags: , ,


    The Project Server 2007 timesheet data population tool has been released on CodePlex:

    The Project Server 2007 Timesheet Data Population Tool enables you to simulate timesheet entries in your farm. This tool can help you perform scalability studies of your PS architecture and validate the sizing of an existing architecture (by measuring timesheet queue throughput for instance). This tool can also be used to test timesheet customization, for instance the Timesheet Tied-mode code sample on Codeplex. This powerful tool should not be run a production environment!

    The Timesheet Data Population tool is a console application written in C#.  It uses standard PSI (Project Server Interface) web service methods to populate timesheets.

    Please refer to the latest version of the Project Server Software Development Kit (SDK) for more information on the PSI methods and how to use them.

    This solution starter was written initially to perform Project Server 2003 vs. Project Server 2007 benchmarks, and has been used since to perform scalability studies and to test timesheet customizations.

    A special thank you to Isabel Bernardos and Steven Haden for helping me write and test the tool during PS 2007 scalability studies for large European customers. 

    If you are aware of any useful tools/solutions for Project and Portfolio Server 2007 and you would like to share them with the community via CodePlex please send me an email.

    About CodePlex

    CodePlex is Microsoft's open source project hosting web site. You can use CodePlex to create new projects to share with the world, join others who have already started their own projects, or use the applications on this site and provide feedback. A word about Microsoft’s role: Microsoft does not control, review, revise, endorse or distribute the third party projects on this site. Microsoft is hosting the CodePlex site solely as a web storage site as a service to the developer community.


    Q: Is the tool supported?

    A: There is no support in terms of CSS/PSS. We expect the support being a CodePlex community effort. Please note that the customization code uses standard supported web service calls available out of the box in EPM2007.

    Q: Is the tool free?

    A: Yes.

    Q: Can I distribute the tool and the source code to customers and partners?

    A: Customers and Partners can use both. Please go to CodePlex to agree on the license terms

    Q: Can a partner distribute the tool and code as is?

    A: No, but partners can point their customer to the website to download it.

    Q: Can a customer install the customization and use it?

    A: Yes the customer can, but he/she is responsible for testing it and running it.

    Q: Can I suggest changes to it?

    A: Yes, join the CodePlex community or send us an email:

    Q: Will this tool be distributed in other ways (i.e. DVDs)?

    A: No.

    Q: What skills do I need to modify or change the tool?

    A: C#, Project Server Interface, and a good understanding of the EPM 2007 data schema.

    Q: I’m trying to modify the code and do have questions. Who do I ask?

    A: Go to the Discussions forums on CodePlex.

    Q: What are all the EPM projects released on CodePlex?

    A. Check this:


    Christophe Fiessinger

  • Project Programmability and Business Intelligence

    Debugging a Project Server Workflow in Visual Studio 2010


    The How to: Install and Test a Project Server Workflow article in the Project 2010 SDK needs information about how to debug a workflow. Because Visual Studio 2010 can install a workflow solution on the local Project Server computer during development and testing, you can use Visual Studio to attach to a process that the workflow uses.

    After you deploy the workflow project in Visual Studio, on the Debug menu, click Attach to Process. In the Attach to Process dialog box, check Show processes from all users and Show processes in all sessions.

    Following are considerations to choose the correct process:

    • If the workflow creates a project or does another operation that uses one of the Queue methods in the PSI (for example QueueCreateProject), the process uses the Project Server Queue Service. In the Attach to Process dialog box, click the Microsoft.Office.Project.Server.Queuing.exe processes.

      Note:   There is a queue process for the application server and for each Project Web App instance. You can attach to all the queue processes, or check the ULS log for the ID of the queue process, and then convert that into a decimal value. For example, if the Microsoft.Office.Project.Server ID is 0x0FFC in the ULS log, the Microsoft.Office.Project.Server.Queuing.exe process ID is 4092.
    • When you submit or restart the workflow, the workflow runs in a w3wp process. Click one or more of the w3wp.exe processes . You can sometimes determine which w3wp process is involved, and find the process ID as in the previous note. Otherwise, attach to all of the w3wp processes.
    • If the workflow uses an approval process that includes many approvers, it might run under the SharePoint timer process. Click the OWSTIMER.EXE process . This is not common.

    When you attach to the correct process, a breakpoint remains as a solid red dot in the left of the code pane. If you do not attach to the correct process, the breakpoint turns into a hollow red circle with yellow caution indicator, and the tooltip for the breakpoint states, “The breakpoint will not currently be hit. No symbols have been loaded for this document.”

    When you click Attach, Visual Studio is ready to catch a breakpoint that you set in the workflow code.

  • Project Programmability and Business Intelligence

    Getting a Project GUID on the Cheap


    Many people have requested for a complementary method to GetProjectNameFromProjectUid where they can pass in a project name and it returns the project's GUID. Unfortunately it did not make it into the Project API. Since it has been requested many times, I figured that it would be useful to post a method, GetProjectUidFromProjectName, which did just that:

    public static Guid GetProjectUidFromProjectName(string projectName)
      Guid projectGUID;

      WSProject.ProjectDataSet readProjDs = projWS.ReadProjectStatus(

      if (readProjDs.Project.Rows.Count == 1)
        projectGUID = new Guid(readProjDs.Project[0].PROJ_UID.ToString());
        throw new Exception("No Project by the name: " + projectName + " Found");

      return projectGUID;


    First thing to note is that projWS is defined elsewhere and it is a connection to the Project Web Service. See my earlier post on Getting Started with the PSI on how to make the connection to the Project Web Service.

    To get the GUID we are going to use the ReadProjectStatus method, as it is the cheapest call to make to get the project's GUID. As you will see, this method takes 4 parameters. The first parameter is the project GUID. In this case, we pass in an empty GUID because that is what we are looking for. Passing in an empty GUID tells Project Server not to search for a project by the GUID.  

    The second parameter is the store to get the project GUID from. Here we have the option of getting the GUID from the working, published or archived store. In this example, I am getting it from the working store, since I want to get the GUID for projects that are actively being worked on and may or may not have been published. Note that this will also get projects that have been published, since they exist in both stores with the same GUID. You may want to change the store based on your requirements.

    The third parameter is the name of the project. This is passed into our GetProjectUidFromProjectName method.

    The last parameter is the type of project. There a number of different project types:

    In most cases 0, which represents standard projects, will be the correct project type.

    The ReadProjectStatus returns a project dataset. If the number of rows returned for the project table is equal to 1, than we have found a project with the given name and we can return the GUID for the project. If the number of rows is not equal to one, than no project by the name was found and we throw an exception.

    Hope this helps,

    Chris Boyd

  • Project Programmability and Business Intelligence

    Project Initiation Phase using InfoPath & Forms Services


    Christophe just passed along some information about a new solution starter on CodePlex:


    A new solution starter that demonstrates how to leverage InfoPath & Forms Services as well Windows Workflow Foundation (hosted by SharePoint) to create your own project initiation phase has been released on CodePlex.

    The project initiation phase is different for every customers and organizations. It typically requires a set of custom attribute to be filled in a web based custom form; and then it needs to go through a custom approval process workflow reviewed by all key decision makers. InfoPath Forms services combined with a SharePoint’s sequential workflow enables you to achieve this business need. This solution starter also demonstrates the added value of deploying Project Server 2007 in a SharePoint Server farm (using InfoPath Forms Services).

    The goal of this solution starter is thus to provide the following two code samples:

    • InfoPath Form with managed code that issues Project Server Interface calls to populate and submit form data
    • SharePoint sequential workflow that leverages the form created earlier to create a custom workflow

    This solution starter requires Project Server 2007, InfoPath and Forms Services 2007, and Visual Studio 2008.

    Step 1 – Fill out and submit Project Initiation Form

    Step 2 – Approve data submitted in Step 1 and create Project in PS 2007

    Resulting data in Project Server 2007

    clip_image001 clip_image002 clip_image003

    This solution starter was created for a presentation I delivered at the 2008 Office Developer Conference.

    If you are aware of any useful tools/solutions for Project and Portfolio Server 2007 and you would like to share them with the community via CodePlex please send me an email.

    About CodePlex

    CodePlex is Microsoft's open source project hosting web site. You can use CodePlex to create new projects to share with the world, join others who have already started their own projects, or use the applications on this site and provide feedback. A word about Microsoft’s role: Microsoft does not control, review, revise, endorse or distribute the third party projects on this site. Microsoft is hosting the CodePlex site solely as a web storage site as a service to the developer community.


    Q: Is the tool supported?

    A: There is no support in terms of CSS/PSS. We expect the support being a CodePlex community effort. Please note that the customization code uses standard supported web service calls available out of the box in EPM2007.

    Q: Is the tool free?

    A: Yes.

    Q: Can I distribute the tool and the source code to customers and partners?

    A: Customers and Partners can use both. Please point them to CodePlex as they have to agree on the license terms

    Q: Can a partner distribute the tool and code as is?

    A: No, but he can point his customer to the website to download it, so he makes sure that the customer agrees with the license terms.

    Q: Can a customer install the customization and use it?

    A: Yes the customer can, but he/she is responsible for testing it and running it.

    Q: Can I suggest changes to it?

    A: Yes, join the CodePlex community or send us an email:

    Q: Will this tool be distributed in other ways (i.e. DVDs)?

    A: No.

    Q: What skills do I need to modify or change the tool?

    A: C#, Project Server Interface, and a good understanding of the EPM 2007 data schema.

    Q: I’m trying to modify the code and do have questions. Who do I ask?

    A: Go to the Discussions forums on CodePlex.

    Q: What are all the EPM projects released on CodePlex?

    A. Check this:


    Christophe Fiessinger

  • Project Programmability and Business Intelligence



    When working with Project Server Events, you may need to reference Microsoft.Office.Project.Server.Schema. Unfortunately, you will not find it in \Program Files\Microsoft Office Servers\12.0\Bin where you can find other common DLLs for Project Server.  For this DLL, you will need to copy it from the GAC:

    1.      Open Visual Studio Command Prompt

    2.      cd %windir%\assembly\GAC_MSIL\Microsoft.Office.Project.Schema\*"

    3.      Copy Microsoft.Office.Project.Schema.dll to a directory outside of the GAC

    4.      Go back to Visual Studio and create a reference to Microsoft.Office.Project.Schema.dll from the directory you copied it to

    Just one thing to keep in mind; you may need to copy the DLL from the GAC again if a Project Server update is installed.

    Hope this helps,


  • Project Programmability and Business Intelligence

    Adding a Project to a Category


    Brian Smith from PSS has passed along this sample that we thought might be helpful:

    The scenario here is that you have a lookup table that shows the categories you want users to select from when creating a project, and then the GUID for the "real" security category is held in the description for the lookup table value.  You make the CF that feeds from the Lookup Table a required Project Level text field.  The Project.Created event fires and the dataset is read - the custom field identified and the GUID of the security category is then used to add the project to the security category.

    No error checking or exception handling is shown - you can do this bit. You would also need to set the categories to the rule "only projects...".  I've hardcoded my lookup table and a reference required to the Microsoft.Office.Project.Server.Library and Events. A Web References to LookupTable, Project, Security and LoginWindows is also required.

    The code will run as the user running the services - so you will either need that account to have PWA permissions or to change to use impersonation.

    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Diagnostics;
    using System.Text;
    using Microsoft.Office.Project.Server.Events;
    using Microsoft.Office.Project.Server.Library;

    namespace TestEventHandler

        public class AutoCategory:ProjectEventReceiver

            public override void OnCreated(PSContextInfo contextInfo, ProjectPostEventArgs e)

                // cfGuid holds CF for Project Category
                Guid cfGuid = new Guid("9bbc698f-5c1d-4f8d-a3d0-163006416bf2");

                // ltGuid holds LT for Categories
                Guid ltGuid = new Guid("625bab60-4427-4f0b-941b-9860d1293338");

                // lt_Struct_Uid gets the id for the selected LT value
                Guid lt_Struct_Uid = new Guid("00000000-0000-0000-0000-000000000000");

                // securityCategoryGuid gets the Security Categorty Guid from the Descriptio field in the lookup table
                Guid securityCategoryGuid = new Guid("00000000-0000-0000-0000-000000000000");

                // 32 is used to just get the CF entities from the readProjectEntities
                const int PROJECT_ENTITY_TYPE_PROJECTCUSTOMFIELD = 32;

                Guid SECURITY_CATEGORY_OBJECT_TYPE_PROJECT = new Guid("1771B1C0-6E26-4FB3-A480-C798AB506E82");

                WebSvcLoginWindows.LoginWindows loginWindows = new TestEventHandler.WebSvcLoginWindows.LoginWindows();
                WebSvcProject.Project project = new TestEventHandler.WebSvcProject.Project();
                WebSvcSecurity.Security security = new TestEventHandler.WebSvcSecurity.Security();
                WebSvcLookupTable.LookupTable lookupTable = new TestEventHandler.WebSvcLookupTable.LookupTable();


                //login to Project Server - this assumes the event service has a login with permissions

                // Impersonation would be better
                loginWindows.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/LoginWindows.asmx";
                loginWindows.Credentials = CredentialCache.DefaultCredentials;

                // Get the dataset
                project.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/Project.asmx";
                project.Credentials = CredentialCache.DefaultCredentials;

                lookupTable.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/LookupTable.asmx";
                lookupTable.Credentials = CredentialCache.DefaultCredentials;

                security.Url = @"https://SERVER_NAME/ProjectServer/_vti_bin/PSI/security.asmx";
                security.Credentials = CredentialCache.DefaultCredentials;

                WebSvcProject.ProjectDataSet dsProjectDataSet = new TestEventHandler.WebSvcProject.ProjectDataSet();

                dsProjectDataSet = project.ReadProjectEntities(e.ProjectGuid, PROJECT_ENTITY_TYPE_PROJECTCUSTOMFIELD, TestEventHandler.WebSvcProject.DataStoreEnum.WorkingStore);

                for (int i = 0; i < dsProjectDataSet.ProjectCustomFields.Count; i++)
                    if (dsProjectDataSet.ProjectCustomFields[i].MD_PROP_UID == cfGuid)
                        lt_Struct_Uid = dsProjectDataSet.ProjectCustomFields[i].CODE_VALUE;

                Guid[] arrayLtUid = new Guid[1]{ltGuid};
                WebSvcLookupTable.LookupTableDataSet dsLookupTable = new TestEventHandler.WebSvcLookupTable.LookupTableDataSet();
                dsLookupTable = lookupTable.ReadLookupTablesByUids(arrayLtUid, false, 1033);

                for (int i = 0; i < dsLookupTable.LookupTableTrees.Count; i++)
                    if (dsLookupTable.LookupTableTrees[i].LT_STRUCT_UID == lt_Struct_Uid)
                        securityCategoryGuid = new Guid(dsLookupTable.LookupTableTrees[i].LT_VALUE_DESC.ToString());

    WebSvcSecurity.SecurityCategoriesDataSet dsSecurityCategories
       = new TestEventHandler.WebSvcSecurity.SecurityCategoriesDataSet();

    // Read the existing values for the security category into the dataset
    dsSecurityCategories = security.ReadCategory(securityCategoryGuid);

                // Get a new objects row to put the created project into

                WebSvcSecurity.SecurityCategoriesDataSet.SecurityCategoryObjectsRow dsSecurityCategoryObjectsRow
    = dsSecurityCategories.SecurityCategoryObjects.NewSecurityCategoryObjectsRow();

                //Set the values
                dsSecurityCategoryObjectsRow.WSEC_OBJ_TYPE_UID = SECURITY_CATEGORY_OBJECT_TYPE_PROJECT;
                dsSecurityCategoryObjectsRow.WSEC_CAT_UID = securityCategoryGuid;
                dsSecurityCategoryObjectsRow.WSEC_OBJ_UID = e.ProjectGuid;

                // Add the row to the dataset and then pass to SetCategories to update

                // Create an EventLog instance and assign its source.
                EventLog myLog = new EventLog();
                myLog.Source = "Project Event Handler";

                // Get information from the event arguments, and

                // write an entry to the Application event log.
                string userName = contextInfo.UserName.ToString();
                string projectName = e.ProjectName.ToString();
                string secCatUid = securityCategoryGuid.ToString();
                int eventId = 3652;
                string logEntry;


                logEntry = "User: " + userName +

                        "\nProject: " + projectName +

                        "\nSecurity Category Uid: " + secCatUid;

                    myLog.WriteEntry(logEntry, EventLogEntryType.Information, eventId);




  • Project Programmability and Business Intelligence

    Does your DEMO laptop with Intel Core i3/i5/i7 CPU bluescreens?


    Thanks to my friend Hans Bellen from UMT and the Microsoft Virtualization Team – we have the final solution for your issues:


    Running Windows 2008 R2 w/ Hyper-V on Core i3/i5/i7 CPU and performance graphic card with WDDM driver installed. After Hyper-v is started the machine bluescreens.


    1. Disable the Intel Turbo Boost in the BIOS or in the Windows 2008R2 if you are dual-booting or the option is not available in the BIOS. To disable the Turbo Boost in Windows Server 2008 R2 – open Device Manager, locate Intel(R) Turbo Boost Technology Driver, disable it.
    2. Get the Windows 2008 R2 SP1 – now available in Beta here

    10/20/10 – UPDATE

    I just have been setting up the Lenovo T410 for the Hands-on Lab for our upcoming Barcelona event and here is some learning:

    • After native nVidia driver from Lenovo site was installed the machine constantly blue screened with Hyper-v enabled even with the “Intel(R) Turbo Boost Technology Driver” disabled
    • Installing SP1 did fix the problem and I’m running my VM and multi-monitor config successfully now
    • Small trick I learned – once you install the nVidia driver you are not able to see anything on the screen or the machine blue screens (nvlddmkm.sys) when booted - to recover – just disable the virtualization in the BIOS and reboot. As Hypervisor service will not run the machine will behave ok and it gives you chance to install SP1 Smile
Page 2 of 11 (255 items) 12345»