Hey all,
I'm hoping someone can point me in the right direction. I have a window service that uses the projectwise APi to login, connect to a datasource, preform various operations to get data from projectwise, including copying files out of the system, then it logs off.
This generally works, but we're having an intermittent problem where it seems our service is being presented with a dialog and thus ceases operation.
The reason I know this is that here is what the stack looks like for the thread in our service that halts because of this dialog.
win32u.dll!NtUserWaitMessage+0x14USER32.dll!DialogBoxIndirectParamAorW+0x403USER32.dll!DialogBoxIndirectParamAorW+0x1b3USER32.dll!SoftModalMessageBox+0x228bUSER32.dll!MessageBoxW+0x348USER32.dll!MessageBoxTimeoutW+0xd5USER32.dll!MessageBoxW+0x4eDMACTRL.dll!aaApi_ShowMessageBox+0x111dmawin.dll!aaOApi_GetVersionLockCount+0x5bbdmsapicl.dll!dmsNetRegisterDisconnectCallback+0x34dmsapicl.dll!dmsNetConnectsReConnect+0x10c0dmsapicl.dll!dmsExecSelectExt+0xb6dmsapicl.dll!dmsExecSelect+0x126dmscli.dll!aaApi_SqlSelect+0xb50x0000000000000000[Native Frame: IL Method without Metadata][Managed to Unmanaged Transition]D:\ILG_Services\Close\ProjectWise.dll!ProjectWise.PWInterface.GetDatabaseServerName+0x4aD:\ILG_Services\Close\ProjectWise.dll!ProjectWise.ExportProject.SetDocumentXMLNodes+0xbcb
Doing some debugging in windbg with a memory dump from our process, I see this string in the thread memory that is likely the message that's presented in the dialog we can't see.
We've changed the credential policy for the account we use to "no expiration". We've also changed the setting to Not show dialogs. Nether seems to have worked here. We do have quite few data sources and this service will connect to each of them if there is data to export, but it will only operate on one at time.
I've opened a case with support today, but havne't heard back yet. I'd appreciate any suggestions on where to look next to get this taken care of. It's really hindering our processing of these projects.
Thanks
So, do the projectwise C API's sort of assume the consumer has an UI? It seems so, because it's trying to display a dialog without knowing if the caller has a UI or not.
Just to make it clear the thread stack above is the only situation which this seems to happen, here is another. Notice that the API calls are different.
win32u.dll!NtUserWaitMessage+0x14USER32.dll!DialogBoxIndirectParamAorW+0x403USER32.dll!DialogBoxIndirectParamAorW+0x1b3USER32.dll!SoftModalMessageBox+0x228bUSER32.dll!MessageBoxW+0x348USER32.dll!MessageBoxTimeoutW+0xd5USER32.dll!MessageBoxW+0x4eDMACTRL.dll!aaApi_ShowMessageBox+0x111dmawin.dll!aaOApi_GetVersionLockCount+0x5bbdmsapicl.dll!dmsNetRegisterDisconnectCallback+0x34dmsapicl.dll!dmsNetConnectsReConnect+0x10c0dmsapicl.dll!dmsSendClientRequest+0x4e3dmsapicl.dll!dmsExecuteFileHandlerCommand+0x3e0dmsapicl.dll!dmsRtvIncrementValue+0xb1dmsapicl.dll!dmsDataSourceDataBufferSelectByServer+0xda2dmsapicl.dll!dmsDataCacheEnable+0x20fdmsapicl.dll!dmsDataCacheEnable+0xb67dmsapicl.dll!dmsDataCacheEnable+0x16b6dmsapicl.dll!dmsProjectResourcesSelect+0x1199dmsapicl.dll!dmsUserSelect+0x47dmscli.dll!aaApi_SelectUser+0x460x0000000000000000[Native Frame: IL Method without Metadata][Managed to Unmanaged Transition]D:\ILG_Services\Close\ProjectWise.dll!ProjectWise.ExportProject.SetDocumentXMLNodes+0xa70D:\ILG_Services\Close\ProjectWise.dll!ProjectWise.ExportProject.CreateDocumentXML+0x122D:\ILG_Services\Close\ProjectWise.dll!ProjectWise.ExportProject.DoDocXML+0x57c
We're still trying to figure out what might be going on here. Support says they're looking for a solution internally. I've verified that all our data sources have No Expiration set for the account in question.We've been unable to make this happen reliably outside of the service it happens in, which is making it very hard to troubleshoot. Sometimes this process takes a long time as there are lots of documents to go through for some projects. Which is why we made sure to use the NoExpiration setting for the PW user we're using.The pseudocode for how we use the API is as follows. We have a wrapper class made up of static dllimport declarations to the SDK and also wrappers that end up calling static members.
aaApi_Initialize(0)
aaApi_AdminLogin(specifying PW Logical account and a datasource)
activedatasource = aaApi_GetActiveDatasource(); //used later to logoff.
//do make other api calls like these. These are sometimes wrapped in a c# class, sometimes called more directly via dllimport.
aaApi_GetProjectNamePath2aaApi_GetDocumentCountaaApi_SelectProjectaaApi_GetProjectStringPropertyaaApi_SelectDocumentaaApi_GetDocumentNumericPropertyaaApi_SelectUseraaApi_GetUserStringProperty
//after we're done, we dispose of our wrapper object using Dispose() in our class. The dispose method calls.
aaApi_LogoutByHandle(activedatasource);
aaApi_Uninitialize();
//Then the whole thing repeats again for another project, if needed.
David,
I don't have a solution for you, I'm just curious.
Is there a reason why your customization could not be run on a schedule, rather than as a service? And if that might work for you, you might also consider using PowerShell (via PWPS_DAB) as that might be a bit easier to maintain or tweak.
As for your service, in our group we have used the PWSession Class found in MostOfDavesClasses in several services and haven't experience the problem you are having. Perhaps the approached used by that Class might be worth exploring.
You can get MostOfDavesClasses from here:https://github.com/DaveBrumbaugh/MostOfDavesClasses-CSharp-Wrappers-For-ProjectWise
You will need to distribute the included .dll's in order to use all of the methods.
Here's a snippet showing how we have used it in a service:
try { using (PWSession pws = new PWSession(sDatasource, sUserName, sUnencryptedPassword)) { foreach (DataRow drSub in drSubmissions) { ... } // for each document in drSubmission } catch (Exception ex) { SomeLoggingClass.WriteLog("Error: {0}\n{1}", ex.Message, ex.StackTrace); }
Hey Dan,
thanks for the reply. Much appreciated! That GitHub repo looks to be very useful and I've been reviewing it. We are handling login\logout very similarly.
After some more experimentation, it seems the issue boils down to this. Our service can be engaging in long running operations that don't involve interaction with projectwise. The way it's coded now is that when it begins an operation, it logs into the datasource and performs some operations, but after that there could be gaps of time (perhaps 10's of minutes) where the connection to projectwise is not used.
In my experimentation, if you log on with the API, then sit idle for more than 13-15 minutes, the connection to projectwise is terminated by the server. Meaning, the TCP connection on port 5800 is lost as the server sent a RST, ACK packet. . After that, if you try to proceed with an operation, assuming you're logged in, it will fail.
We would expect the connection to remain open, but for some reason the server is terminating it if it's idle.
So it seems given this behavior we need to re-code the projecwise interaction to login-->do projectwise specific operations-->logout and repeat when necessary.
Unless, there is a more nuanced way that would involve less re-coding. Maybe some sort of "keep alive" algorithm we could employ. Or is there a server side setting controlling this?
Lastly, support did tell me that you can get rid of the login dialog if you use aaAPI_SetModuleFlags. That works fine, but obviously doesn't solve the connection issue.