I wrote a folder commnd to create a bunch of empty documents in this folder which has an environment, and then update doument's attributes values ( actually 4 attributes) as soon as the document is created. So far everying works fine except that sometimes one attribute ( "LookupType") value becomes strange characters. The problem is random, in most times, no error at all for all docus, so I can't reproduce it at will, and I noticed only this attribute has problem. The difference between this attribute "LookupType" and other 3 attributes is this attribute can use pulldown value. But my value for this attribute is one of these pulldown values..
Here is my code.
docID = CreateLookupFile(lProjfolderId, CString(areacode), CString(description));
if (docID !=0)
if (FALSE == UpdateDocAttrAll(lProjfolderId,docID,sParentProjName,sProjName,CString areacode),CString(description)))
ShowErrorMessage (nResCode, L"Failed to update attributes");
//press F5!, so we can see new doc appears one by one..
BOOL UpdateDocAttrAll(LONG lProjNo, LONG lDocNo, LPCWSTR strValue1, LPCWSTR strValue2, LPCWSTR strValue3, LPCWSTR strValue4)
BOOL bRtn = TRUE;
LONG lLinkCount = aaApi_SelectLinks(lProjNo,lDocNo);
for (LONG i = 0L; i < lLinkCount; i++)
LONG lTableId = aaApi_GetLinkNumericProperty(LINK_PROP_TABLEID,i);
LONG lLinkColumnId = aaApi_GetLinkNumericProperty(LINK_PROP_COLUMNID,i);
LPCWSTR strLinkColValue = aaApi_GetLinkStringProperty(LINK_PROP_COLUMN_VALUE,i);
//here lColumnId is hard coded!
aaApi_UpdateLinkDataColumnValue(lTableId, 1, strValue1);
aaApi_UpdateLinkDataColumnValue(lTableId, 2, strValue2);
aaApi_UpdateLinkDataColumnValue(lTableId, 3, strValue3);
aaApi_UpdateLinkDataColumnValue(lTableId, 4, strValue4);
if (!aaApi_UpdateLinkData(lTableId, lLinkColumnId, strLinkColValue))
bRtn = FALSE;
This random problem really frustrated me, please help me out, thanks in advance.
I suspect that your problem is because you have not initialized the buffer before you put values into it and you are getting random values in the buffer that is causing your problem. In fact, you are calling that function after you have written the values.
The correct way of creating or updating user attributes is basically to call the correct initialization function first, fill the buffer with values, write the buffer.
For creating (INSERT) attribute rows, you use these steps:
To modify (UPDATE) an attribute row, you use these steps:
aaApi_FreeLinkDataUpdateDesc()aaApi_UpdateLinkDataColumnValue()aaApi_UpdateEnvAttr() – or – aaApi_UpdateLinkData()
There are also some "helper" functions that help you format your data depending on what you are doing. Check out
And I strongly discourage "hard coding" anything into code. Go get those values with code, so that if you need to use your program for another datasource (such as a test datasource) and the underlying values don't match (and they might not match), your code still "works" right.
Thanks Dan for your prompt reply.
Following your instruction, I moved the aaApi_FreeLinkDataUpdateDesc() before aaApi_UpdateLinkDataColumnValue, but got error the first time run my command after I started up PW Explorer! Out of 160 documents, some document's this attribute values are strange character, some are good, some empty, some messed up. But as I said earlier, only this attribute "LookupType" got errors, other 3 attributes are always good. Then I deleted all documents, re-run my command, then no error, everything looks correct! So it seems some buffer is not initialized the first time after I start up PWE?? I don't know..
Actually in this environment table, all 4 attritues are type nvarchar, attribute "Scheme" value is selected folder's parent project folder name, "Number" and "Description" are values queried from other Database by ODBC, but they never got error. The attribute "LookupType" that got error is the selected folder name, and it's short, like "FBS".
So problem is unsolved, still looking for your help. Thanks again
My apologies for not looking closer at your code.
If you look at the documentation for aaApi_UpdateLinkDataColumnValue(), you will find that it is not the function that you should be using to update user attributes.
I know that the documentation isn't necessarily easy to understand if you are not familiar with the history of the product, but it does say " for updating attribute environment tables use function aaApi_UpdateEnvAttr()", which is what I attempted to point out in my previous post.
Change your code to follow this approach and it should work:
aaApi_FreeLinkDataUpdateDesc()aaApi_UpdateLinkDataColumnValue() // call once for each value you want to changeaaApi_UpdateEnvAttr() – or – aaApi_UpdateLinkData()
Hope this helps!
Here's how to update one or more user attributes for a given document (assuming that you know the values for the document's project id (typically called lProjectId) and document id (typically called lDocumentId).
First get the information about the Environment. Use aaApi_GetEnvTableInfoByProject() to determine the table id and the id of the column number that holds the value of the identifier for the row. This is KEY.
Next use aaApi_SelectColumnsByTable() to determine the column ids of the ones you want to update.
Then select the actual links to the attribute rows to update using aaApi_SelectLinks().
The for EACH link (each one is a row in the environment table and is one SHEET in ProjectWise Explorer), follow those 3 steps.
Then you should get what you are expecting.
Here's a modified version of the exercise for learning how to work with user attributes from the SDK training class. I would strongly recommend that you (and anyone else new to the SDK) take the training, not because I teach it, but because it will save you a lot of time and effort (grief) if you learn the "basics" first and understand how the APIs are layered, the general approach, history (legacy), etc. And of course, if you haven't done so already, please at least take the ProjectWise Administrator training first and ideally the ProjectWise User training before that. You can easily "break" functionality in ProjectWise Explorer if you don't understand what it does as the APIs allow you to break the rules (which of course can be exactly what you want to do).
Here's the modified code. Keep in mind that the column names are "hard coded", and the values set are based on the lecture, but this is to keep the exercise manageable for the class exercise. In the "real" world, you most likely would do these steps in a way that makes sense for your application.
LONG lEnvId = 0L;
LONG lTableId = 0L;
LONG lAttrColId = 0L; // the column id that holds the attribute id's value
if (!aaApi_GetEnvTableInfoByProject (*(pProjects+i), &lEnvId, &lTableId, &lAttrColId))
L"Could not get environment info.",
L"Problem with selecting environment info using project id.");
LONG lo_string10ColId = 0L;
LONG lo_string40ColId = 0L;
LONG lColCount = aaApi_SelectColumnsByTable( lTableId );
L"Could not get column info.",
L"Table appears to have no columns!");
else if (lColCount==-1)
L"Problem with selecting column info using table id.");
// sentinal value of zero...
lo_string10ColId = 0L;
lo_string40ColId = 0L;
for (LONG j=0; j<lColCount; j++)
if (_tcsicmp(_T("o_string10"), aaApi_GetColumnStringProperty( COLUMN_PROP_NAME, j)) == 0)
lo_string10ColId = aaApi_GetColumnNumericProperty( COLUMN_PROP_COLUMN_ID, j);
else if (_tcsicmp(_T("o_string40"), aaApi_GetColumnStringProperty( COLUMN_PROP_NAME, j)) == 0)
lo_string40ColId = aaApi_GetColumnNumericProperty( COLUMN_PROP_COLUMN_ID, j);
if ((lo_string10ColId==0) || (lo_string40ColId==0))
L"Could not determine column ids.",
L"Environment table is missing one or more target columns.");
LONG lLinkRows = aaApi_SelectLinks(*(pProjects+i), *(pDocuments+i));
L"Could not select document sheets.",
L"In the real world, we would also want to know the PW error here.");
else if (0==lLinkRows)
// no rows to update - skip this document...
for (LONG k=0; k<lLinkRows; k++)
if (!aaApi_UpdateLinkDataColumnValue (lTableId, lo_string10ColId, _T("Updated!")))
L"Could not populate update buffer for o_string10.",
memset (szDSName,'\0', sizeof(szDSName)/sizeof(WCHAR));
if (!aaApi_UpdateLinkDataColumnValue (lTableId, lo_string40ColId, szDSName))
L"Could not populate update buffer for o_string40.",
To update the existing row, we need to know the value of the
a_attrno column. We can get that value with
aaApi_GetLinkStringProperty (LINK_PROP_COLUMN_VALUE, k).
LPCWSTR lpcwstrColumnValue = aaApi_GetLinkStringProperty (LINK_PROP_COLUMN_VALUE, k);
if (!aaApi_UpdateLinkData(lTableId, lAttrColId, lpcwstrColumnValue))
L"Could not update attribute row.",
You might find it easier to create a new environment, add the columns used in the exercise and try this code against documents using that test environment.
Answer Verified By: Wence