When I want to connect to PW in Access with aaApi_LoginDlg (0, 0, 0, 0, 0, 0), no Datasource is displayed.
Identical VBA code works perfectly with Excel. it's amazing that this external dll doesn't work the same with Excel and Access.
My IT thinks it's because the SDK doesn't know where the data source server is, but it doesn't have the solution.
Didier,
This function has been deprecated, but it should still work.
Take a look at MostOfDavesClasses to see how it is used in C#.
As for not getting a list of datasources, that's something you can check with ProjectWise Explorer. If the machine you are running your code on isn't configured to query specific sources for datasources, ProjectWise Explore (and the API functions), won't "see" any datasources (unless UDP is enabled and the ProjectWise server machine and client are on the same network).
Hello, thank youTouching UDP does not change anything.
So I tried to search the data sources to choose the user, then log it.
Call :
Option Explicit Public Declare Function aaApi_SelectDatasourcesByServer2 Lib "dmscli.dll" ( _ ByVal ulFlags As Long, _ ByVal strServerAddr As Long, _ ByVal ulType As Long) As Long Public Declare Function aaApi_GetDatasourceName Lib "dmscli.dll" (ByVal lIndex As Long) As Long Public Declare Function aaApi_Login Lib "dmscli.dll" (ByVal lDsType As Long, _ ByVal lpctstrDSource As Long, _ ByVal lpctstrUser As Long, _ ByVal lpctstrPassword As Long, _ ByVal lpctstrSchema As Long _ ) As Long Global oDocument As clsPWDocument 'Public Const ServeurPW = "projectwise-ss4.egis.fr" error DNS Public Const ServeurPW = "10.129.20.5" Sub Lancement() Dim Result As Boolean Set oDataSource = New clsPWDataSource Result = oDataSource.LoginDataSource() Stop End Sub
And in the class module
Public Function LoginDataSource() As Boolean Dim Nombre As Long Dim lOk As Long Dim strptrNomServeur As Long Dim NomServeur As String Dim strptrDataSource As Long Dim NomDataSource(1000) As String Dim NbDataSource As Long Dim NumDataSource As Long Dim lpctstrDSource As Long Dim lIndex As Long ' Récupère le nom des DataSources NomServeur = ServeurPW ' C'est une constante, donc intégrée au code : StrPtr ne peux pas la trouver strptrNomServeur = StrPtr(NomServeur) LoginDataSource = True Nombre = aaApi_SelectDatasourcesByServer2(0, strptrNomServeur, 0) Select Case Nombre Case Is = 0 MsgBox "Pas de DataSource trouvée" Stop LoginDataSource = False Exit Function Case Is = -1 lOk = aaApi_ShowLastErrorMessageBox ' renvoie un strptr sur le message de l'erreur Stop LoginDataSource = False Exit Function End Select NbDataSource = Nombre For lIndex = 0 To Nombre - 1 strptrDataSource = aaApi_GetDatasourceName(lIndex) NomDataSource(lIndex + 1) = VBStringFromPtrW(strptrDataSource) Next lIndex ' Ecran de choix ' resultat NumDataSource ' Connect lpctstrDSource = StrPtr(NomDataSource(NumDataSource)) lOk = aaApi_Login(0, lpctstrDSource, 0, 0, 0) If lOk = 0 Then MsgBox "Erreur login" Stop LoginDataSource = False End If End Function
aaApi_SelectDatasourcesByServer2 answer "File not found C:\program Files\Bentley\ProjectWise\bin\dmscli.dll"
I checked, dmscli is present and contains the called function.
It looks like dmscli is calling another function that is not found.
But where it is very strange is that the same code works under excel. How is it possible ?
I'm not sure why the file wasn't found assuming that the path is correct, but there is a mismatch on the signature of the function compared to how you declared it.
The actual signature looks like this:
You declared the function to have a three signed longs as parameters, which doesn't match the documentation, so perhaps Windows doesn't recognize the entry point and can't "find" the function?
Here's how you would declare it using C#:
Now, I'm not an expert in "VBA", so perhaps three longs will work, but since the second long is really a pointer to a "string", your code would have to take that into account so that the ProjectWise API function interprets it as a string, and from what I can tell, it looks like you are doing that with a call to StrPtr.
I can't really help you with VBA to WIN32 issues as VBA isn't something I have any "real" experience with. The ProjectWise API works from C, C++, and can be made to work with other languages such as C# if you correctly "map" the API function signatures. Perhaps someone else can help you with using VBA interacting with unmangaged code. Sorry I can't help you more on this part.
Dan,
VBA is much simpler than C, it does not intrinsically know the subtleties such as unsigned integer, memory pointer, or even what UNICODE is.
LONG, ULONG, PLONG, LPWSTR and even BOOLEAN, which occupy four bytes in memory are seen by VBA as Long. In VBA, Null represents absolutely nothing, while the NULL of C is a zero on 4 bytes.
To process UNICODE, you must call Kernel32 functions, being careful to reserve memory for the aaAapi functions which return strptr (UNICODE occupies ANSI * 2 +1 bytes).
The kernel of my code, almost 2,000 lines, is in four class modules that call almost 80 aaAapi, and in Excel everything works.
To write my code, I was largely inspired by what is here: using-the-projectwise-api-in-vba, although this code has a lot of bugs and logic errors.
I think there is a bug in dmscli (I'm pointing to the 64-bit version), which crashes when called from Access. It's incomprehensible.
Hi Didier POUZET,
To get to the bottom of this issue quickly I think 2 checks are in order to confirm first, then 1 very specific need justified after.
Two checks
One need
If the above cannot help resolve the issue(s), it is recommended to reduce code and data to the minimum and zip provide a "test case" for review/proof (w/o guessing - portable and hopefully reproducible).
Although not our domain and support expertise, we do provide these Microsoft support topics/articles that may also provide more insights to help resolve the issue(s): Debugging VBA and Troubleshooting.
HTH,Bob
Office is fine in 32 bits. Excel and Access have the necessary permissions, the problem is not there.
I attach an Excel file and an access file, stripped down, comprising exactly the same code
You will see for yourself that aaAPI works in Excel and not in Access.
You may need to go to options / security to allow macros.
Test login with Excel ans Access.zip
Thanks for your help.
Here is the full code:
Option Explicit ' Kernel ************************************************************* Public Declare Function lstrlenW Lib "kernel32" (lpString As Any) As Long Public Declare Function WideCharToMultiByte Lib "kernel32" (ByVal codepage As Long, _ ByVal dwFlags As Long, _ lpWideCharStr As Any, _ ByVal cchWideChar As Long, _ lpMultiByteStr As Any, _ ByVal cchMultiByte As Long, _ ByVal lpDefaultChar As String, _ ByVal lpUsedDefaultChar As Long _ ) As Long ' Standart login *************************************************************** Public Declare Function aaApi_GetActiveDatasource Lib "dmscli.dll" () As Long Public Declare Function aaApi_LoginDlg Lib "dmawin.dll" (ByVal lDsType As Long, _ ByRef dsName As Long, _ ByVal lDSLength As Long, _ ByVal user As Long, _ ByVal pwd As Long, _ ByVal Schema As Long) As Long Public Declare Function aaApi_ShowLastErrorMessageBox Lib "DMACTRL.dll" () As Long ' Alternative login *************************************************************** Public Declare Function aaApi_SelectDatasourcesByServer2 Lib "dmscli.dll" ( _ ByVal ulFlags As Long, _ ByVal strServerAddr As Long, _ ByVal ulType As Long) As Long Public Declare Function aaApi_GetDatasourceName Lib "dmscli.dll" (ByVal lIndex As Long) As Long Public Declare Function aaApi_Login Lib "dmscli.dll" (ByVal lDsType As Long, _ ByVal lpctstrDSource As Long, _ ByVal lpctstrUser As Long, _ ByVal lpctstrPassword As Long, _ ByVal lpctstrSchema As Long _ ) As Long ' Project to check ******************************************************************** Public Declare Function aaApi_SelectProjectDlg Lib "dmawin.dll" (ByVal hWndParent As Long, _ ByVal lpctstrTitle As Long, _ ByVal lProjectId As Long _ ) As Long Public Declare Function aaApi_SelectProject Lib "dmscli.dll" (ByVal lProjectId As Long) As Long Public Declare Function aaApi_GetProjectStringProperty Lib "dmscli.dll" ( _ ByVal lPropertyId As Long, _ ByVal lIndex As Long _ ) As Long ' Divers *********************************************************************************** Public Const PROJ_PROP_NAME As Long = 12 ' String property. ' Retour de boite de dialogue Public Const DLG_OK As Long = 1 ' Tout c'est bien passé Public Const DLG_CANCEL As Long = 2 ' L'action a été annulée par l'utilisateur. Public Const DLG_ABORT As Long = 3 ' Ca s'est planté Public Const ID_OK As Long = 1 ' Tout c'est bien passé (n'est pas toujours en retour) Public Const ID_NONE As Long = 0 ' L'objet demandé n'existe pas Public Const ID_ABORT As Long = -1 ' Ca s'est planté (n'est pas toujours en retour Public Const CP_ACP = 0 Public Const MAX_STRINGLEN As Long = 1024 ' Au pif, pas trouvé dans la doc 'Public Const ServeurPW = "projectwise-ss4.egis.fr" d'ont work, error DNS Public Const ServeurPW = "10.129.20.5" ' Internal Egis ' **************************************************************************** ' Pour l'écran de choix Global strDataSourceName2(20) As String Global strDataBaseDesigned As String ' ************************************************************************** Sub ClassicalLogin() ' Standart method Dim lngPointeurDataSource As Long Dim lOk As Long Dim strLen As Long Dim ptrDataSourceName As Long Dim strDataSourceName As String Dim strTitre As String Dim ptrTitre As Long Dim lProjectId As Long Dim strProjectName As String Dim ptrProjectName As Long Stop ' Here is the code entry point ' Press F8 to advance step by step ' Press F5 to execute the code directly '<-- To the right of any instruction, click in the vertical bar here to set a breakpoint ' (display of a red circle) lngPointeurDataSource = aaApi_GetActiveDatasource() If lngPointeurDataSource <> 0 Then ' If you're still connected, don't ask again Else lOk = aaApi_LoginDlg(0, 0, 0, 0, 0, 0) ' Default datasource Select Case lOk Case Is = DLG_OK ' On est toujours connecté Case Is = DLG_ABORT lOk = aaApi_ShowLastErrorMessageBox MsgBox "Echec login" Stop End Case Is = DLG_CANCEL MsgBox "Cancel login" Stop End Case Else Stop End End Select End If strTitre = "Designate a directory" ptrTitre = StrPtr(strTitre) lProjectId = 0 ' Default lOk = aaApi_SelectProjectDlg(0, ptrTitre, lProjectId) Select Case lOk Case Is > 0 lProjectId = lOk Case Is = ID_ABORT lOk = aaApi_ShowLastErrorMessageBox Stop End Case Else MsgBox "Echec du répertoire" Stop End End Select ' Put the project in the static buffer lOk = aaApi_SelectProject(lProjectId) Select Case lOk Case Is = ID_OK Case Is = ID_ABORT lOk = aaApi_ShowLastErrorMessageBox Stop End Case Is = ID_NONE MsgBox "No project sélected" Stop End Case Else Stop End End Select ' Get the pointer to the UNICODE name of the directory ptrProjectName = aaApi_GetProjectStringProperty(PROJ_PROP_NAME, 0) ' Get the ANSI string which is there strProjectName = VBStringFromPtrW(ptrProjectName) MsgBox "Project number : " & lProjectId & vbCrLf & _ "Project name : " & strProjectName End Sub ' ************************************************************************* Sub AltenativeLogin() Dim Nombre As Long Dim lOk As Long Dim ptrNomServeur As Long Dim strNomServeur As String Dim ptrDataSourceName2 As Long Dim lIndex As Long Stop ' Here is the code entry point ' Press F8 to advance step by step ' Press F5 to execute the code directly '<-- To the right of any instruction, click in the vertical bar here to set a breakpoint ' (display of a red circle) strNomServeur = ServeurPW ' It's a constant, so it's built into the code: StrPtr can't find it ptrNomServeur = StrPtr(strNomServeur) Nombre = aaApi_SelectDatasourcesByServer2(0, ptrNomServeur, 0) Select Case Nombre Case Is = 0 MsgBox "Pas de DataSource trouvée" Stop End Case Is = -1 lOk = aaApi_ShowLastErrorMessageBox Stop End End Select If Nombre > 21 Then Nombre = 21 For lIndex = 0 To Nombre - 1 ptrDataSourceName2 = aaApi_GetDatasourceName(lIndex) strDataSourceName2(lIndex) = VBStringFromPtrW(ptrDataSourceName2) Next lIndex strDataBaseDesigned = "" ChooseDataBase.Show ' Waiting for user End Sub Sub ReplyComboBox() Dim ptrDataBaseDesigned As Long Dim lOk As Long Dim strTitre As String Dim ptrTitre As Long Dim lProjectId As Long Dim strProjectName As String Dim ptrProjectName As Long Stop ' Here is the second code entry point ' Press F8 to advance step by step ' Press F5 to execute the code directly '<-- To the right of any instruction, click in the vertical bar here to set a breakpoint ' (display of a red circle) If strDataBaseDesigned = "" Then MsgBox "La DataBase n'a pas été désignée" Stop End End If ptrDataBaseDesigned = StrPtr(strDataBaseDesigned) lOk = aaApi_Login(0, ptrDataBaseDesigned, 0, 0, 0) If lOk = ID_NONE Then lOk = aaApi_ShowLastErrorMessageBox Stop End End If strTitre = "Designate a directory" ptrTitre = StrPtr(strTitre) lProjectId = 0 ' Default lOk = aaApi_SelectProjectDlg(0, ptrTitre, lProjectId) Select Case lOk Case Is > 0 lProjectId = lOk Case Is = ID_ABORT lOk = aaApi_ShowLastErrorMessageBox Stop End Case Else MsgBox "Echec du répertoire" Stop End End Select lOk = aaApi_SelectProject(lProjectId) Select Case lOk Case Is = ID_OK Case Is = ID_ABORT lOk = aaApi_ShowLastErrorMessageBox Stop End Case Is = ID_NONE MsgBox "No project sélected" Stop End Case Else Stop End End Select ptrProjectName = aaApi_GetProjectStringProperty(PROJ_PROP_NAME, 0) strProjectName = VBStringFromPtrW(ptrProjectName) MsgBox "Project number : " & lProjectId & vbCrLf & _ "Project name : " & strProjectName End Sub ' ************************************************************************* Public Function VBStringFromPtrW(ptrstrW As Long) As String ' Returns an ANSI string from a ptrstrW pointer to a Unicode string Dim strRtn As String Dim Len_ptrstrW As Long If ptrstrW = 0 Then Stop VBStringFromPtrW = "" Exit Function End If Len_ptrstrW = lstrlenW(ByVal ptrstrW) ' Longueur de la chaîne associée au pointeur, exprimé en caractères strRtn = String$(Len_ptrstrW * 2 + 1, 0) ' On réserve la mémoire pour accueillir le résultat (2 bytes/char) ' Demande à Kernel32 de faire la conversion et de donner le résultat dans strRtn ' WideCharToMultiByte retourne également la longueur de chaîne Unicode Call WideCharToMultiByte(CP_ACP, 0, ByVal ptrstrW, -1, ByVal strRtn, Len(strRtn), 0, 0) If InStr(strRtn, vbNullChar) Then ' On ne prends que ce qui est avant le NULL VBStringFromPtrW = Left$(strRtn, InStr(strRtn, vbNullChar) - 1) Else ' Si strRtn n'a pas de caractère null, la fonction Left$ ci-dessus renvoit une ' chaîne de longueur nulle (""). VBStringFromPtrW = strRtn End If End Function
To test Excel and Access, simply designate your ProjectWise server in the PWServer constant