How to create/update attributes for a document using c#?

I wanted to create documents and set their attributes in ProjectWise from a C# application. I could create documents but I don't know how to set the attributes. I'm trying to use aaApi_SetLinkDataColumnValue and aaApi_CreateLinkData but it does not work.

Parents
  • Hi Dan,

    Thanks for your reply. To use CreateEnvAttr(), I need to define  AAEALINKAGE structure in c# and I cannot get this part to work. I was wondering if you can help me with this.

    Thanks again

  • This function is difficult to do in C#.  The LPAAEALINKAGE is a pointer to a structure which contains a pointer to a structure.  You are passing a managed nested structure to an un-managed function.   Each structure must be tagged with [StructLayout(LayoutKind.Sequential)], each variable must be the correct size and you have to allocate the instance of the structures with Marshal.AllocHGlobal to prep the memory to be sent to an unmanaged function.  I have only used this function from C++, not C#.  Don't forget to release the allocated memory.

  • Darius,

    Thanks for stating what versions are involved.

    It isn't clear to me the context of what you are attempting to do.  When you say ".. run same code from PW client", what does that mean?  How are you invoking your code and how was it built?  If ProjectWise Explorer is calling your code via a menu item, or a hook, etc. it needs to appear to be a native call, so if your code is in a DLL that is compiled as a .NET library, that will fail.

    Also, any function called by ProjectWise Explorer needs to be 32-bit, or at least designed to be called from a 32-bit process.  Take a look at the sample I provided that demonstrates how to call a .NET method from ProjectWise Explorer.  

    https://github.com/DanWilliamsAtBentleyDotCom/Sample-Calling-CSharp-Code-From-Mrr-File

    The "key" is to use [DllExport] just before your public static method, and then in the post-build process, use DllExporterNet4.exe to make it callable from unmanaged code.

    One last problem, I have no idea what the "PWWrapper" class you are referencing contains as that is a "common" name folks give to their own specific wrapper class, so there are variations "out in the wild".  Are you using MostOfDavesClasses?  If so, aaApi_CreateEnvAttr() is commented out in the current version of that class.

    You can try the "other" way of creating a new attribute row.  Here's a C++ example from the solution to one of the exercises in the ProjectWise SDK training class where I'm demonstrating the "other" way:

    See if that approach works for you.  If not, please post more details of how you are building your code as well as how it is being called by ProjectWise Explorer.

  • Thank you for help Dan,

    Let me clarify. 

    Yes, I use most of Dave classes. (PWWrapper)

    I run successfully this code from basic windows form.

    When I generate dll and call it from PW explorer then I have this problem. Target to x86, the code runs except this function.

    I can create new document for example.

    pwUp = PWWrapper.aaApi_CreateDocument(ref targetdocId, destFolderIdInt, storageID, fileType, PWWrapper.DocumentType.Normal,
    applicationID, departmentId, wProfileID, syncFilepath, form.newfilename,
    form.newfilename, description, "_.1", false, PWWrapper.DocumentCreationFlag.NoAttributeRecord,
    sbworkFile, 256, ref attributeID);

    I use your mentioned dll export 

    My goal of using c# that I not just run PW API, but mix it with other. It is more complicated in c++ use different database or applications APIs.  

    Best regards

    Darius 

  • You don't need to convince me of the advantages of using .NET, the "problem" of course is calling unmanaged code, i.e. the PW APIs from managed code, or managed code from unmanaged code, i.e. calling a .NET method from ProjectWise Explorer.  So again, how are you calling your managed code from ProjectWise Explorer?

    If "it works" from "basic windows form", whatever that means, and it doesn't "work" when you call the same "code" from ProjectWise Explorer, it seems to me that the reason is likely to be due to the "context" of the invocation of your code.

    Please post some screenshots and code snippets to help me understand what you are doing and what might be the reason(s) behind the "it works here but not there" issue.

    In an attempt to help you along, here is a method I have used that is in C#, and uses MostOfDavesClasses that will create or update attribute rows for a document.  You will have to make some assumptions about what my class "DocumentCandidate" contains, as well as the DataRow I am passing to this method, but otherwise, you should be able to following the logic and use of MostOfDavesClasses objects OK. FWIW, in this particular case, the DataRow object, "dr" is from a database table that is being used to create or update ProjectWise documents.

            /// <summary>
            /// Updates or creates User Attributes.
            /// </summary>
            /// <param name="curDoc">Current candidate document.</param>
            /// <param name="dr">Current DataRow from database view.</param>
            /// <returns></returns>
            private static bool UpdateAttributes( DocumentCandidate curDoc, DataRow dr )
            {
                int iEnvId = 0;
                int iTableId = 0;
                int iColumnId = 0;
    
                //  TODO: need to account for hard-coded-rules
    
                if (!PWWrapper.aaApi_GetEnvTableInfoByProject(curDoc.ProjectId, ref iEnvId, ref iTableId, ref iColumnId))
                {
                    LogIt.WriteLogError(string.Format("Could not determine attribute information for {0}",
                        PWWrapper.GetDocumentNamePath(curDoc.ProjectId, curDoc.DocumentId)));
                    LogIt.WriteLogOnlyPwError();
                    return false;
                }
    
                if (!PwEnvironmentMapping.EnvMap.ContainsKey(iEnvId))
                {
                    LogIt.WriteLogError("Environment for this document is not mapped in the XML Mapping file.");
                    return false;
                }
    
                PwEnvironment curEnv = PwEnvironmentMapping.EnvMap[iEnvId];
    
                // check for 0, 1 or multiple existing attribute rows...
                int iNumLinks = PWWrapper.aaApi_SelectLinks(curDoc.ProjectId, curDoc.DocumentId);
    
                if (iNumLinks < 0)
                {
                    LogIt.WriteLogError("Problem selecting links for this document.  Please see log file for details.");
                    LogIt.WriteLogPwError();
                    return false;
                }
                
                if (iNumLinks == 0)
                {
                    // no existing rows, create one
                    bool bAtLeastOneInvalidValue = false;
                    bool bAtLeastOneUpdatedBufferValue = false;
    
                    PWWrapper.aaApi_FreeLinkDataInsertDesc();
    
                    // update buffer with values
                    foreach (KeyValuePair<string, PwEnvironmentColumn> kvp in curEnv.PwEnvironmentColumnByColumnName)
                    {
                        // skip any columns that are not mapped
                        if (string.IsNullOrEmpty(kvp.Value.SourceColumnName))
                        {
                            continue;
                        }
    
                        string sAttrValue = dr[kvp.Value.SourceColumnName].ToString().Trim();
    
                        // use default value if provided
                        if (string.IsNullOrEmpty(sAttrValue))
                        {
                            if (!string.IsNullOrEmpty(kvp.Value.DefaultValue))
                            {
                                sAttrValue = kvp.Value.DefaultValue;
                            }
                        }
    
                        // skip if the source value is null or use default value if provided
                        if (string.IsNullOrEmpty(sAttrValue))
                        {
                            continue;
                        }
    
                        // validate string for SQL data type
                        switch (ValidStringForSqlType(ref sAttrValue, kvp.Value.SqlType, kvp.Value.Length, kvp.Value.Name, kvp.Value.SourceColumnName))
                        {
                            case 0:     // OK
                                break;
    
                            case 1:     // warning
                                break;
    
                            default:    // error
                            {
                                bAtLeastOneInvalidValue = true;
                                continue;
                            }
                        }
    
                        // update buffer
                        if (!PWWrapper.aaApi_SetLinkDataColumnValue(iTableId, kvp.Value.ColumnId, sAttrValue))
                        {
                            LogIt.WriteLogError("Problem writing db value '{0}' to buffer '{1}'.  Please see log file for details.", sAttrValue, kvp.Value.Name);
                            LogIt.WriteLogOnlyPwError();
                        }
                        else
                        {
                            bAtLeastOneUpdatedBufferValue = true;
                        }
                    }
    
                    if (!bAtLeastOneUpdatedBufferValue)
                    {
                        // no row needed to be created, so return true if no other errors
                        if (bAtLeastOneInvalidValue)
                        {
                            return false;
                        }
                        
                        return true;
                    }
    
                    // create the new link row
                    int iLinkColId = 0;
                    StringBuilder sbVal = new StringBuilder(30);
    
                    if (!PWWrapper.aaApi_CreateLinkDataAndLink(iTableId, 1, curDoc.ProjectId, curDoc.DocumentId, ref iLinkColId, sbVal, sbVal.Capacity))
                    {
                        LogIt.WriteLogError("Could not create attributes for {0}", curDoc.Name);
                        LogIt.WriteLogOnlyPwError();
                        return false;
                    }
                    
                    // row created, so return true if no other errors
                    if (bAtLeastOneInvalidValue)
                        return false;
                    
                    return true;
                }
                else
                {
                    // document has existing link rows - default values and environment set to create attribute records upon document creation...
                    bool bAtLeastOneInvalidValue = false;
                    bool bAtLeastOneRowFailed = false;
    
                    for (int iRow = 0; iRow < iNumLinks; iRow++)
                    {
                        string sUniqueVal = PWWrapper.aaApi_GetLinkStringProperty(PWWrapper.LinkProperty.ColumnValue, iRow);
    
                        // prepare to update an existing row
                        bool bAtLeastOneUpdatedBufferValue = false;
    
                        PWWrapper.aaApi_FreeLinkDataUpdateDesc();
    
                        foreach (KeyValuePair<string, PwEnvironmentColumn> kvp in curEnv.PwEnvironmentColumnByColumnName)
                        {
                            // skip any columns that are not mapped
                            if (string.IsNullOrEmpty(kvp.Value.SourceColumnName))
                            {
                                continue;
                            }
    
                            string sAttrValue = dr[kvp.Value.SourceColumnName].ToString().Trim();
    
                            // skip if the source value is null
                            if (string.IsNullOrEmpty(sAttrValue))
                            {
                                continue;
                            }
    
                            // validate string for SQL data type
                            switch (ValidStringForSqlType(ref sAttrValue, kvp.Value.SqlType, kvp.Value.Length, kvp.Value.Name, kvp.Value.SourceColumnName))
                            {
                                case 0:     // OK
                                    break;
    
                                case 1:     // warning
                                    break;
    
                                default:    // error
                                {
                                    bAtLeastOneInvalidValue = true;
                                    continue;
                                }
                            }
    
                            // update buffer
                            if (!PWWrapper.aaApi_UpdateLinkDataColumnValue(iTableId, kvp.Value.ColumnId, sAttrValue))
                            {
                                LogIt.WriteLogError("Problem writing db value '{0}' to buffer '{1}'.  Please see log file for details.", sAttrValue, kvp.Value.Name);
                                LogIt.WriteLogOnlyPwError();
                            }
                            else
                            {
                                bAtLeastOneUpdatedBufferValue = true;
                            }
    
                        }
    
                        // update the row if there was anything put into the buffer
                        if (bAtLeastOneUpdatedBufferValue)
                        {
                            if (!PWWrapper.aaApi_UpdateLinkData(iTableId, iColumnId, sUniqueVal))
                            {
                                LogIt.WriteLogError("Could not update attributes for {0}",
                                    PWWrapper.GetDocumentNamePath(curDoc.ProjectId, curDoc.DocumentId));
                                LogIt.WriteLogOnlyPwError();
                                bAtLeastOneRowFailed = true;
                            }
                        }
    
                    }
    
                    if (bAtLeastOneRowFailed || bAtLeastOneInvalidValue)
                        return false;
                    
                    return true;
                }
            }
    

    Hopefully, this will help you get pass whatever the issue is with your code.  However, your issue may still be something about how you are calling your code from ProjectWise Explorer as my code is from a standalone application written in C#.

  • Hi,

    It is interesting, but it started to work.

    So my code looks like this. I call it from file context menu, not figured out how to run it from folder context menu.

    [DllExport]
    public static int TyrNewFileFromTemplate
    (
    uint uiCount, //==>Count of documents
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]int[] plProjArray, //==>Project number Array
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]int[] plDocArray //==> Document number Array
    //==> Document number Array
    )
    {

    //Check in file without attribute values
    pwUp = PWWrapper.aaApi_CreateDocument(ref targetdocId, destFolderIdInt, storageID, fileType, PWWrapper.DocumentType.Normal,
    applicationID, departmentId, wProfileID, syncFilepath, form.newfilename,
    form.newfilename, description, "_.1", false, PWWrapper.DocumentCreationFlag.NoAttributeRecord,
    sbworkFile, 256, ref attributeID);

    int envId = 0;
    int lTableId = 0;
    int columnId = 0;
    PWWrapper.aaApi_GetEnvTableInfoByProject(destFolderIdInt, ref envId, ref lTableId, ref columnId);
    errorPW = PWWrapper.aaApi_GetLastErrorDetail();
    int lColCount = PWWrapper.aaApi_SelectColumnsByTable(lTableId);
    PWWrapper.aaApi_FreeLinkDataInsertDesc();

    createSetlink = PWWrapper.aaApi_SetLinkDataColumnValue(lTableId,
    PWWrapper.aaApi_GetColumnNumericProperty(PWWrapper.ColumnProperty.ColumnID, j), value);
    errorPW = PWWrapper.aaApi_GetLastErrorDetail();

    int lAttrFileId = 0;
    PWWrapper._AAEALINKAGE aAEALINKAGE = new PWWrapper._AAEALINKAGE();
    PWWrapper._AADOC_ITEM aADOC_ITEM = new PWWrapper._AADOC_ITEM();
    aADOC_ITEM.lDocumentId = destFolderIdInt;
    aADOC_ITEM.lProjectId = targetdocId;
    aAEALINKAGE.documentId = aADOC_ITEM;
    aAEALINKAGE.lEnvironmentId = envId;
    aAEALINKAGE.lLinkageType = 1;

    int columnid = 0;
    int lptstrValueBuffer = 0;

    It started to work after I use this command

    System.Text.StringBuilder pwFilenamea = new System.Text.StringBuilder(form.newfilename, 256);
    bool createlink2 = PWWrapper.aaApi_CreateLinkDataAndLink(lTableId, 1, destFolderIdInt, targetdocId, ref columnid, pwFilenamea, lptstrValueBuffer);
    errorPW = PWWrapper.aaApi_GetLastErrorDetail();

    Then aaApi_CreateEnvAttr works fine 

    bool createlink1 = PWWrapper.aaApi_CreateEnvAttr(lTableId, aAEALINKAGE, ref lAttrFileId);
    errorPW = PWWrapper.aaApi_GetLastErrorDetail();

    Best regards

    Darius

Reply Children
No Data