aaApi_CreateProject is not working

Hello,

I cannot figure out what is wrong with the following code. Do anyone has idea?

This always returns False.

bool createProj(int projectID, LPCTSTR name, LPCTSTR description)
{

LONG projectID2 = 0L;
LONG lngWorkSpaceID = aaApi_GetWorkspaceProfileId(projectID, 0);
LONG userID = aaApi_GetCurrentUserId();
LONG lStorageId = aaApi_GetStorageId(0);

BOOL resultCreate = aaApi_CreateProject(

&projectID2,
projectID,
lStorageId,
userID,
AADMS_PROJECT_TYPE_NORMAL,
0,
lngWorkSpaceID,
0,
name,description);

return resultCreate;

}

Parents
  • Did you check to see what is on the ProjectWise error stack? Use aaApi_GetLastErrorId(), etc. to determine why the call failed.

    Also, you aren't showing in this call that you actually filled the static buffers you are using for aaApi_GetWorkspaceProfileId() or aaApi_GetStorageId() which will return a value which may or may not be what you think unless you first filled the buffers based on your needs. See the topic "ProjectWise Data Buffers" under "ProjectWise Developer's Guide" under "Modules" in the SDK help for more about using static and dynamic buffers in ProjectWise.
  • Dan, Thanks for advise. I added aaApi_GetLastErrorId() and got error id 58002. Can you advise where is the error id table? Thank you!
  • Hongseok,

    Sorry, thought it was obvious. Use the other error related calls to get the rest of the error information. In ProjectWise, there are three parts to an error, the id, the message and the detail. Look in the help under "Modules" > "ProjectWise API" > "Error Handling Functions" and you will see that you can get the other two parts as well as the id.

    aaApi_GetLastErrorId()
    aaApi_GetLastErrorMessage()
    aaApi_GetLastErrorDetail()

    TIP:  You can sometimes find specific error codes in the header files.  Try greping them.  For your error code, 58002, I used Notepad++ to search for the regular expression "\#define.*58002" and found this:

    #define AAERR_DMS_ERR_INVALID_ARG  58002

    Which tells me that what I suspected, that one of the values you are getting from a static buffer (that you didn't first fill) is  probably invalid or perhaps one of the parameters that you are passing to your function (parent id, name or description).

  • I would check the values you are passing and ensure that none are zero or -1 (other than ProjectId2). aaApi_GetStorageId needs aaApi_SelectStoragesForProject(ProjectId) , executed prior to it to fill the AADMSBUFFER_STORAGE internal buffer.

  • Thank you so much! It now works.

    bool createProj(int projectID, LPCTSTR name, LPCTSTR description)
    {

    LONG projectID2 = 0L;
    LONG lngWorkSpaceID = aaApi_GetWorkspaceProfileId(projectID, 0);
    LONG userID = aaApi_GetCurrentUserId();
    LONG storageindex = aaApi_SelectStoragesForProject(projectID);
    LONG lStorageId = aaApi_GetStorageId(storageindex);

    BOOL resultCreate = aaApi_CreateProject(
    &projectID2,
    projectID,
    lStorageId,
    userID,
    AADMS_PROJECT_TYPE_NORMAL,
    0,
    0,
    0,
    name,
    description);

    return resultCreate;

    }
  • i know this is pretty old but in case anyone else is trying to use this...

    the line 

    LONG lStorageId = aaApi_GetStorageId(storageindex);

    cant use the storageindex that gets returned from the line above.

    that returned value is the count of storages. the get storage api call is zero based. so the index being used is always out of bounds. 

  • Good point John!

    When I teach the ProjectWise SDK class, I try to emphasize always looking at the documentation, partly because if you are new to using the SDK you shouldn't "guess" of course, but also because there are some "inconsistencies" in the return types.  So as you get familiar with the APIs, you may start feeling confident in predicting the return type, so sometimes you may guess the wrong return type but the compiler will catch that, but more "dangerous" is misunderstanding what the return value means once you start making assumptions (not verify against the documentation).

    A long while back now, an effort was made to change many of the functions to return BOOL to indicate that the call was successful or not, instead of some "special" meanings to a LONG value might return.  I don't remember any examples from way back then, but you could see that a call to aaApi_GetConnectedUsers() could return a count, and then you could iterate though a buffer to get the information for each one.  Now aaApi_GetConnectedUsers() actually returns a BOOL and a count is returned via a passed parameter.

    It makes sense to use a LONG to indicate a count of items returned such as for most "select" functions.  These typically return a LONG as a "count", but sometimes a pointer to a buffer, from which you typically get a count via aaApi_DmsDataBufferGetCount(HAADMSBUFFER hDataBuffer).

    But there as some subtle differences in what a LONG value may mean when it is both a count or an error flag.

    If you look at aaApi_SelectAllStorages(VOID) or aaApi_SelectStoragesForProject(LONG projected), the LONG return value indicates the count (zero or positive number), or an error flag, -1, which means that the error is on the ProjectWise error stack and it is up to you to go look at it and decide what to do because of a particular error.

    But if you look at aaApi_SelectStorage(LONG lStorageId), it returns 1 if it was able to select the storage area information, 0 if it failed to select the storage area information because it doesn't exist but nothing is placed on the error stack.  But if the call failed for any other reason, it returns -1 and the reason is on the ProjectWise Error stack!  So slight difference between the meaning of a returned value of zero between these two functions that return a LONG.

    And as you pointed out, typically when you see word "index", you can pretty much assume that the data you want is in a buffer that you had to previously select and is NOT an actual value.

    FWIW, the ProjectWise APIs have many "shorthand" functions for one reason or another.  For example, aaApi_GetStorageId(LONG lIndex) is really just a "helper" function for aaApi_GetStorageNumericProperty(LONG lPropertyId, LONG lIndex) where the value of lPropertyId is predetermined to be STORAGE_PROP_ID, but both are getting the value from a buffer that had to be first populated.

    aaApi_SelectStorage(LONG lStorageId) fills a buffer with the properties for a specific storage area, while aaApi_SelectAllStorages() fills a buffer with the information for all storage areas, and as you pointed out, the index into the buffer always starts with zero.

Reply
  • Good point John!

    When I teach the ProjectWise SDK class, I try to emphasize always looking at the documentation, partly because if you are new to using the SDK you shouldn't "guess" of course, but also because there are some "inconsistencies" in the return types.  So as you get familiar with the APIs, you may start feeling confident in predicting the return type, so sometimes you may guess the wrong return type but the compiler will catch that, but more "dangerous" is misunderstanding what the return value means once you start making assumptions (not verify against the documentation).

    A long while back now, an effort was made to change many of the functions to return BOOL to indicate that the call was successful or not, instead of some "special" meanings to a LONG value might return.  I don't remember any examples from way back then, but you could see that a call to aaApi_GetConnectedUsers() could return a count, and then you could iterate though a buffer to get the information for each one.  Now aaApi_GetConnectedUsers() actually returns a BOOL and a count is returned via a passed parameter.

    It makes sense to use a LONG to indicate a count of items returned such as for most "select" functions.  These typically return a LONG as a "count", but sometimes a pointer to a buffer, from which you typically get a count via aaApi_DmsDataBufferGetCount(HAADMSBUFFER hDataBuffer).

    But there as some subtle differences in what a LONG value may mean when it is both a count or an error flag.

    If you look at aaApi_SelectAllStorages(VOID) or aaApi_SelectStoragesForProject(LONG projected), the LONG return value indicates the count (zero or positive number), or an error flag, -1, which means that the error is on the ProjectWise error stack and it is up to you to go look at it and decide what to do because of a particular error.

    But if you look at aaApi_SelectStorage(LONG lStorageId), it returns 1 if it was able to select the storage area information, 0 if it failed to select the storage area information because it doesn't exist but nothing is placed on the error stack.  But if the call failed for any other reason, it returns -1 and the reason is on the ProjectWise Error stack!  So slight difference between the meaning of a returned value of zero between these two functions that return a LONG.

    And as you pointed out, typically when you see word "index", you can pretty much assume that the data you want is in a buffer that you had to previously select and is NOT an actual value.

    FWIW, the ProjectWise APIs have many "shorthand" functions for one reason or another.  For example, aaApi_GetStorageId(LONG lIndex) is really just a "helper" function for aaApi_GetStorageNumericProperty(LONG lPropertyId, LONG lIndex) where the value of lPropertyId is predetermined to be STORAGE_PROP_ID, but both are getting the value from a buffer that had to be first populated.

    aaApi_SelectStorage(LONG lStorageId) fills a buffer with the properties for a specific storage area, while aaApi_SelectAllStorages() fills a buffer with the information for all storage areas, and as you pointed out, the index into the buffer always starts with zero.

Children
  • I fell into the storageindex trap! Thank you Dan for these details.
    But I have a problem, the new project does not inherit the environment, it is <none>, there is no attributes. By creating the directory manually, it works.
    It is lWorkspaceProfileId ?
    aaApi_GetWorkspaceProfileId on the parent return 0 (this function does not require a buffer).
    Using the result of aaApi_GetProjectNumericProperty (PROJ_PROP_ENVIRONMENTID, 0) does nothing (after aaApi_SelectProject).
    I can't figure out how to do it.

  • Creating the "Project" (actually just a folder in this case -  "words get in the way" again) with aaApi_CreateProject() doesn't do more than just create the folder.  If you want the new folder to use an environment, you have to add it with a second API call such as aaApi_ModifyProject2().

    You can call one API function, aaApi_CreateProject2(), to do most of the things you want, but you have to first allocate and populate a buffer (AADMSPROJITEM).  Typically you select the parent folder and then inspect its properties to determine what to use for the child folder you want to create, such as the environment, storage area, etc. and then you can set the corresponding properties (and flags) in the AADMSPROJITEM structure that you allocated.

    As I mention in the SDK training, some things that you think are just one function call, actually involve multiple calls, and others that you would expect to be multiple API functions, sometimes have a single function, you just have to do all the work setting up all the parameters that it expects to be passed to it.

    And lWorkspaceProfileId is for, well workspaces!

    HTHs

  • Thank you, but i still have a problem.

    aaApi_CreateProject2 responds:"error creating a new folder, Name required as parameter for this function"

        ' On rempli la structure descriptive
        DonneesProject.guidVault = 0
        DonneesProject.lComponentClassId = 0
        DonneesProject.lComponentInstanceId = 0
        DonneesProject.lEnvironmentId = EnvironnementID ' 102
        DonneesProject.lManagerId = 0
        DonneesProject.lManagerType = 0
        DonneesProject.lParentId = ParentProjectID ' 107
        DonneesProject.lProjectId = 0 ' ce qu'on cherche
        DonneesProject.lptstrDesc = strptrDescriptionProject ' 211555732
        DonneesProject.lptstrName = strptrNomProject ' 209048980
        DonneesProject.lStorageId = lStorageId ' 1
        DonneesProject.lTypeId = AADMS_PROJECT_TYPE_NORMAL
        DonneesProject.lWorkflowId = 0
        DonneesProject.lWorkspaceProfileId = 0
        DonneesProject.projFlagMask = 0
        DonneesProject.projFlags = 0
        Drapeaux = AADMSPROJF_ENVID Or AADMSPROJF_PARENTID Or AADMSPROJF_PROJECTID _
                                Or AADMSPROJF_DESC Or AADMSPROJF_NAME _
                                Or AADMSPROJF_STORAGEID Or AADMSPROJF_TYPEID
        DonneesProject.ulFlags = Drapeaux
        ptrDonneesProject = VarPtr(DonneesProject)
        HeriterControleAcces = 0
    
        ' Création du répertoire
        ' Note : PW ne rafraichit pas l'affichage
        lOK = aaApi_CreateProject2(ptrDonneesProject, HeriterControleAcces)
        If lOK = 0 Then
            lOK = aaApi_ShowLastErrorMessageBox ' renvoie un strptr sur le message de l'erreur
            CreerProject = 0
            Stop
        Else
            CreerProject = DonneesProject.lProjectId ' La valeur de retour
    

    What name does it need, it has the name of the directory?

  • I would have to see your actual code to determine what the problem is, but I suspect that either you have not included the bit mask AADMSPROJF_NAME in the ulFlags property of the AADMSPROJITEM structure, or your project name is invalid.  It isn't clear to me what programming language you are using.

    Perhaps an example would help?  Here's a snippet of C++ code that I have used before:

            AADMSPROJITEM   PrjInfo;
    
            PrjInfo.ulFlags = AADMSPROJF_PROJECTID|AADMSPROJF_ENVID|AADMSPROJF_PARENTID|AADMSPROJF_STORAGEID|AADMSPROJF_MANAGERID|AADMSPROJF_TYPEID|AADMSPROJF_WORKFLOW|AADMSPROJF_NAME|AADMSPROJF_DESC|AADMSPROJF_MGRTYPE|AADMSPROJF_WSPACEPROFID;
            PrjInfo.lProjectId = 0L;
            PrjInfo.lEnvironmentId = ENVNO_NON_ENG;
            PrjInfo.lParentId = glPrjId;
            PrjInfo.lStorageId = glPrjStorageId;
            PrjInfo.lManagerId = glPrjManagerId;
            PrjInfo.lTypeId = glPrjTypeId;
            PrjInfo.lWorkflowId = glPrjWorkflowId;
            PrjInfo.lptstrName = CSTR_NON_ENG;
            PrjInfo.lptstrDesc = CSTR_NON_ENG;
            PrjInfo.lManagerType = glPrjManagerType;
            PrjInfo.lWorkspaceProfileId = glPrjWorkspaceProfileId;
    
            if( !aaApi_CreateProject2( &PrjInfo, -1 ) )
            {
                glNonEngPrjId = 0L;
                strErrMsg.Format
                    (
                    _T("Could not create the subfolder \"%s\" for PrjNo:%ld:%s\n"), 
                    CSTR_NON_ENG, 
                    glPrjId, 
                    gstrPrjName
                    );
                strErrMsg.Format( _T("[%ld] %s %s\n"), aaApi_GetLastErrorId(), aaApi_GetLastErrorMessage(), aaApi_GetLastErrorDetail() );
            }
            else
            {
                glNonEngPrjId = PrjInfo.lProjectId;
                bFoundNonEng = TRUE;
            }
    

  • No, there is no shortage of flags

    I am using Visual Basic in Excel. Apart from the syntax, it is basically not very different from C ++

    #define

    Type AADMSPROJITEM ' la structure est utilisée pour représenter les données de l'élément de projet.
        guidVault As Long ' Spécifie le GUID du projet.
        lComponentClassId As Long ' Spécifie l'ID de classe de l'instance ODS contenant les propriétés
                                  ' de projet riches.
        lComponentInstanceId As Long ' Spécifie l'ID d'instance de l'instance ODS contenant les propriétés de
                                     ' projet riches.
        lEnvironmentId As Long ' Spécifie l'environnement auquel appartient le projet.
        lManagerId As Long ' Spécifie l'ID du chef de projet.
        lManagerType As Long ' Spécifie le type de chef de projet.
                             ' Voir Types de projets et de gestionnaires de documents pour les valeurs
                             ' prédéfinies.
        lParentId As Long ' Spécifie l'identifiant du projet parent.
        lProjectId As Long ' Spécifie l'ID du projet.
        lptstrDesc As Long ' Pointeur vers une chaîne terminée par un caractère nul spécifiant
                           ' la description du projet.
        lptstrName As Long ' Pointeur vers une chaîne terminée par un caractère nul spécifiant le nom du projet.
        lStorageId As Long ' Spécifie le stockage par défaut du projet.
        lTypeId As Long ' Spécifie le type de projet.
                        ' Voir Types de projets pour plus d'informations.
        lWorkflowId As Long ' Spécifie l'identificateur de workflow attribué au projet.
        lWorkspaceProfileId As Long ' Spécifie le type de profil d'espace de travail de projet.
        projFlagMask As Long ' Spécifie les bits valides dans les projFlags membres.
        projFlags As Long ' Spécifie les indicateurs de projet (flags) à définir ou à réinitialiser.
        ulFlags As Long ' Ce membre est le masque de bits spécifiant lequel des membres de la structure
                        ' contient des informations valides.
                        ' Reportez-vous à AADMSPROJF_* pour une liste détaillée des valeurs possibles.
    End Type
    

    the apostrope is worth //

    flags

    ' Définitions des indicateurs (flags) AADMSPROJITEM
    'Public Const AADMSPROJF_ALL As Long = &HFFFF ' S'il est défini, tous les membres contiennent des
                                                 ' informations valides.
    'Public Const AADMSPROJF_GUID As Long = &H1000 ' S'il est défini, le membre guidVault contient des
                                                  ' informations valides.
    'Public Const AADMSPROJF_COMPONENT_CLASSID As Long = &H2000 ' S'il est défini, le membre lComponentClassId
                                                               ' contient des informations valides.
    'Public Const AADMSPROJF_COMPONENT_INSTANCEID As Long = &H8000 ' S'il est défini, le membre
                                                                  ' lComponentInstanceId contient des
                                                                  ' informations valides.
    Public Const AADMSPROJF_ENVID As Long = &H2 ' S'il est défini, le membre lEnvironmentId contient des
                                                ' informations valides.
    'Public Const AADMSPROJF_MANAGERID As Long = &H10 ' S'il est défini, le membre lManagerId contient des
                                                     ' informations valides.
    'Public Const AADMSPROJF_MGRTYPE As Long = &H400 ' S'il est défini, le membre lManagerType contient des
                                                    ' informations valides.
    Public Const AADMSPROJF_PARENTID As Long = &H4 ' S'il est défini, le membre lParentId contient des
                                                   ' informations valides.
    Public Const AADMSPROJF_PROJECTID As Long = &H1 ' S'il est défini, le membre lProjectId contient des
                                                    ' informations valides.
    Public Const AADMSPROJF_DESC As Long = &H100 ' S'il est défini, le membre lptstrDesc contient des
                                                 ' informations valides.
    Public Const AADMSPROJF_NAME As Long = &H80 ' S'il est défini, le membre lptstrName contient des
                                                ' informations valides.
    Public Const AADMSPROJF_STORAGEID As Long = &H8 ' S'il est défini, le membre lStorageId contient des
                                                    ' informations valides.
    Public Const AADMSPROJF_TYPEID As Long = &H20 ' S'il est défini, le membre lTypeId contient des
                                                  ' informations valides.
    Public Const AADMSPROJF_WORKFLOW As Long = &H40 ' S'il est défini, le membre lWorkflowId contient des
                                                    ' informations valides.
    'Public Const AADMSPROJF_WSPACEPROFID As Long = &H800 ' S'il est défini, le membre lWorkspaceProfileId
                                                         ' contient des informations valides.
    'Public Const AADMSPROJF_PROJFLAGS As Long = &H4000 ' S'ils sont définis, les membres projFlagMask et
                                                       ' projFlags contiennent des informations valides.
    ' ulFlags est forcément valide, obligatoire

    &H means 0x

    the function

    Public Function CreerProject(ParentProjectID As Long, EnvironnementID As Long, WorkFlowID As Long, _
                    Niveau As Integer, NomProject As String, DescriptionProject As String) As Long
    ' Crée un nouveau projet dans PW. Appelé par l'objet Project.
    
    Dim strptrNomProject As Long
    Dim strptrDescriptionProject As Long
    Dim SousProjectID As Long
    Dim UserId As Long
    Dim StorageIndex As Long
    Dim lStorageId As Long
    Dim SousNiveau As Integer
    Dim ValeurZero As Long
    Dim lOK As Long
    Dim EstRacine As Boolean
    Dim AvecDocuments As Boolean
    Dim AvecProprietes As Boolean
    'Dim Nombre As Long
    
    Dim DonneesProject As AADMSPROJITEM
    Dim ptrDonneesProject As Long
    Dim HeriterControleAcces As Long
    Dim Drapeaux As Long
        
        ' Récupération des infos
        ValeurZero = 0
        If Trim(NomProject) = "" Then
            strptrNomProject = 0
            MsgBox "Il faut un nom au répertoire"
            Stop
        Else
            strptrNomProject = StrPtr(NomProject) ' Sous_Repertoire_1
        End If
    
        If Trim(DescriptionProject) = "" Then
            strptrDescriptionProject = 0
        Else
            strptrDescriptionProject = StrPtr(DescriptionProject)
        End If
        SousProjectID = 0
        UserId = aaApi_GetCurrentUserId()
        StorageIndex = aaApi_SelectStoragesForProject(ParentProjectID)
        Select Case StorageIndex
        Case Is = -1
            lOK = aaApi_ShowLastErrorMessageBox ' renvoie un strptr sur le message de l'erreur
            Stop
        Case Is = 0
            MsgBox "Eléments de stockage introuvables"
            Stop
        End Select
        lStorageId = aaApi_GetStorageId(ValeurZero) ' On prends le premier (base 0)
        If lStorageId = 0 Then
            MsgBox "Stokage introuvable"
            Stop
        End If
    
        ' On rempli la structure descriptive
        DonneesProject.guidVault = 0
        DonneesProject.lComponentClassId = 0
        DonneesProject.lComponentInstanceId = 0
        DonneesProject.lEnvironmentId = EnvironnementID ' 102
        DonneesProject.lManagerId = 0
        DonneesProject.lManagerType = 0
        DonneesProject.lParentId = ParentProjectID ' 107
        DonneesProject.lProjectId = 0 ' ce qu'on cherche
        DonneesProject.lptstrDesc = strptrDescriptionProject
        DonneesProject.lptstrName = strptrNomProject ' 209048980
        DonneesProject.lStorageId = lStorageId ' 1
        DonneesProject.lTypeId = AADMS_PROJECT_TYPE_NORMAL ' 0
        DonneesProject.lWorkflowId = WorkFlowID ' 1
        DonneesProject.lWorkspaceProfileId = 0
        DonneesProject.projFlagMask = 0
        DonneesProject.projFlags = 0
    
        Drapeaux = AADMSPROJF_ENVID ' 2
        Drapeaux = Drapeaux Or AADMSPROJF_PARENTID ' 4
        Drapeaux = Drapeaux Or AADMSPROJF_PROJECTID ' 1
        If strptrDescriptionProject <> 0 Then Drapeaux = Drapeaux Or AADMSPROJF_DESC ' 256
        Drapeaux = Drapeaux Or AADMSPROJF_NAME ' 128
        Drapeaux = Drapeaux Or AADMSPROJF_STORAGEID ' 8
        Drapeaux = Drapeaux Or AADMSPROJF_TYPEID ' 32
        Drapeaux = Drapeaux Or AADMSPROJF_WORKFLOW ' 64
        DonneesProject.ulFlags = Drapeaux ' 239
        
        ptrDonneesProject = VarPtr(DonneesProject)
        HeriterControleAcces = 0 ' ça veut dire oui
    
        ' Création du répertoire
        ' Note : PW ne rafraichit pas l'affichage
        lOK = aaApi_CreateProject2(ptrDonneesProject, HeriterControleAcces)
        If lOK = 0 Then
            lOK = aaApi_ShowLastErrorMessageBox ' renvoie un strptr sur le message de l'erreur
            CreerProject = 0
            Stop
        Else
            CreerProject = DonneesProject.lProjectId ' La valeur de retour
    
            ' Mettre le project dans la collection
            EstRacine = False ' Forcément
            AvecDocuments = False
            AvecProprietes = False
            lngCountLevel = Niveau + 1
            CreeObjetProjet SousProjectID, EstRacine, AvecDocuments, AvecProprietes
        
        End If
    
    End Function

    Thank you Dan for your help. without you we would be wrong.!