Intermittent Error while getting WorksetInfo from a BIM 360 Model - vb.net

I'm using Autodesk.Revit.DB.WorksharingUtils.GetUserWorksetInfo(ModelPath) to get a list of Worksets from a model. This has worked flawlessly on local server and Revit Server projects for several years. This year we are adding capabilities to link BIM 360 models. So now I'm querying a model in BIM 360. Sometimes it works without error. But every once in a while it fails and I see in the journal file that it fails on this method.
I suspect that it is a timing thing or maybe the file in BIM 360 hasn't completely finished updating from previous activities (like syncing and closing). But I don't know how to deal with that possibility. I've tried pausing the program to see if that helps. It doesn't.
Here's essentially what I'm doing:
`' open the host model
aDoc = OpenCloudModelWithCallback(mApp, modelPath)
' Open a Transaction
Dim mTransaction As New Transaction(aDoc)
mTransaction.Start("Linking Models")
'Revit Server (and BIM 360) cannot use relative path
Dim linkOptions As New RevitLinkOptions(True)
linkOptions.IsRelative = False
' FileInfo is a local class created to store information about Revit models
For Each f As FileInfo In LinkList
' Don't load "Shared Levels and Grids"
Dim linkedWorksets As IList(Of WorksetPreview) = WorksharingUtils.GetUserWorksetInfo(f.Modelpath)
Dim worksetIds As IList(Of WorksetId) = New List(Of WorksetId)()
' Find worksetName
For Each worksetPrev As WorksetPreview In linkedWorksets
If worksetPrev.Name.CompareTo("Shared Levels and Grids") <> 0 Then
worksetIds.Add(worksetPrev.Id)
End If
Next
Dim linkWorksetConfig As New WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets)
linkWorksetConfig.Open(worksetIds)
linkOptions.SetWorksetConfiguration(linkWorksetConfig)
'link in the BIM 360 model
linkResult = RevitLinkType.Create(aDoc, f.Modelpath, linkOptions)
Next
' End Transaction
mTransaction.Commit()
Any ideas on what might be causing this? Or how I might troubleshoot an intermittent error?

Related

Same Winforms .Net binary uses much more memory on one PC and doesn't release it (GC not working maybe?)

I have a problem with an application that's driving me crazy, and I really don't know what else I can do to investigate further.
I've got a simple Windows Forms application (32-bit, VB.net, Framework 4.5.1) behaving very strangely on only one PC, regarding memory allocation. On all other PCs I tried it's working fine! There MUST be something on that particular PC, but I'm at a loss how to figure out what it is, short of reinstalling everything one by one...
So, on all PCs except one, the memory utilization of the application is fine and doesn't go above 10-20 Megabytes. On one PC only, which happens to be my own development PC, the memory keeps increasing and never gets released, until it reaches the 2 GB Windows limit and throws an Out of Memory exception. I copied the exact same binaries on 6 different PCs (four Win7, two Win10), and confirmed that the application is behaving fine (and consistently) on all those PCs. On my own PC only, even when the application starts without initiating the transfer, it seems to be occupying at least 20 megabytes more compared to other PCs. So whatever it is, must be something on the PC which is causing this.
The app is using an SqlDataReader with SqlBulkCopy to transfer several gigabytes of data from one database to another. The transfer is done month by month. All the SqlDataReader, SqlBulkCopy, SqlConnection and SqlCommand objects are disposed after each month, since I'm enclosing them in a Using block. I'm even calling GC.Collect at the end of each iteration.
I even used Visual Studio's memory profiler to try and figure out what's happening, but all it's saying is that the majority of the memory is requested by the SqlBulkCopy.WriteToServer command. This doesn't seem to be particularly insightful.
Now here's another interesting note: If I use the Winapi method EmptyWorkingSet after every iteration (not shown in code below), the memory seems to be getting released. So why on earth doesn't Dotnet's Garbage Collector do the same, as expected and why only on one PC???
Here's the code. Some parts modified to remove confidential stuff.
It's a VB.Net Winforms application using .Net Framework 4.5.1. It's build on VS 2017, Windows 10.
Any advise on how on earth to investigate this further, short of starting to remove Windows Updates and other software one-by-one from that particular workstation, would be greatly appreciated.
Many thanks!
Private Sub CopyData()
' Database connection strings for source and destination db
Dim conn_string_source As String = "<CONFIDENTIAL DATA MASKED>",
conn_string_destination As String = "<CONFIDENTIAL DATA MASKED>"
' For progress report.
Dim rows_total As Long,
rows_done As Long
'
' Find total rows to transfer.
' For progress report.
'
Using _
conn_src As New SqlConnection(conn_string_source),
cmd As New SqlCommand("Select Count(*) From dbo.SOURCE_TABLE", conn_src)
rows_total = CLng(cmd.ExecuteScalar())
Console.WriteLine("{0:#,##0} total rows to transfer.", rows_total)
End Using
'
' Transfer rows for one year, one month at a time.
' Dispose connections, readers and bulkcopy class at end of each month.
'
For which_month = 1 To 12
Using _
conn_dest As New SqlConnection(conn_string_destination),
conn_src As New SqlConnection(conn_string_source),
bc = New SqlClient.SqlBulkCopy(conn_dest)
'
' For progress report.
' Calculate rows for this month.
'
Dim rows_this_month As Long
Using cmd = New SqlCommand("Select Count(*)
From dbo.SOURCE_TABLE
Where posting_month = #mm", conn_src)
cmd.Parameters.AddWithValue("#mm", which_month)
rows_this_month = cmd.ExecuteScalar()
End Using
'
' Setup the Bulk Copy operation
'
bc.DestinationTableName = "dbo.TARGET_TABLE"
bc.BulkCopyTimeout = 0 ' Never timeout
bc.BatchSize = 3000 ' Rows per batch. This values seems efficient enough.
bc.NotifyAfter = 250 ' Raise notification event after every this number of rows copies.
' Microsoft says for better memory usage, though tests indicate doesn't make difference.
bc.EnableStreaming = True
' Notify us every few rows to know how much was finished.
AddHandler bc.SqlRowsCopied,
Sub(sndr, rr)
Console.WriteLine(" ...{0:#,##0} / {1:#,##0}", rr.RowsCopied, rows_this_month)
End Sub
'
' Perform the actual transfer.
' Feed a reader to the SqlBulkCopy operation.
'
Using cmd As New SqlCommand("Select *
From dbo.SOURCE_TABLE
Where posting_month = #mm", conn_src)
cmd.Parameters.AddWithValue("#mm", which_month)
Using rdr = cmd.ExecuteReader()
bc.WriteToServer(rdr)
End Using
End Using
rows_done += rows_this_month
Console.WriteLine(" GRAND TOTAL: {0:#,##0} / {1:#,##0}", rows_done, rows_total)
End Using
GC.Collect()
Next
End Sub

Word automation failing on server, but dev station works great

I am automating a oft-used paper form by querying the user on a web page, then modifying a base Word document and feeding that modified doc file to the user's browser for hand-off to Word.
The code is Visual Basic, and I am using the Microsoft.Office.Interop module to manipulate the document by manipulating Word. Works fine on the development system (Visual Studio 2015) but not on the production server (IIS 8.5).
Both the Documents.Open() call and the doc.SaveAs() call fail with Message="Command failed" Source="Microsoft Word" HResult=0x800A1066
Things I've tried:
Added debugging out the whazoo: Single-stepping is not an option on the production machine, so I pinpointed the problem lines with debug output.
Googled and found that this problem has been reported as early as 2007, but no viable solutions were reported.
A couple sites mentioned timing issues, so I added several pauses and retries -- none helped.
Some mentioned privileging, so I tried changing file permissions & application pool users -- neither helped.
Enhanced my exception handling reports to show more details and include all inner exceptions. That yielded the magic number 800A1066 which led to many more google hits, but no answers.
Added fall-back code: if you can't open the main document, create a simple one. That's when I found the SaveAs() call also failing.
Dropped back to the development system several times to confirm that yes, the code does still work properly in the right environment.
Greatly condensed sample code does not include fallback logic. My Word document has a number of fields whose names match the XML tokens passed as parameters into this function. saveFields() is an array of those names.
Dim oWord As Word.Application
Dim oDoc As Word.Document
oWord = CreateObject("Word.Application")
oWord.Visible = True
oDoc = oWord.Documents.Open(docName)
Dim ev As String
For i = 0 To saveFields.Length - 1
Try
ev = dataXD.Elements(saveFields(i))(0).Value
Catch
ev = Nothing
End Try
If ev IsNot Nothing Then
Try
Dim field = oDoc.FormFields(saveFields(i))
If field IsNot Nothing Then
If field.Type = Word.WdFieldType.wdFieldFormTextInput Then
field.Result = ev
End If
End If
Catch e As Exception
ErrorOut("Caught exception! " & e.Message)
End Try
End If
Next
...
oDoc.SaveAs2(localDir & filename)
oDoc.Close()
oWord.Quit(0, 0, 0)
The code should generate a modified form (fields filled in with data from the parameters); instead it fails to open, and the fallback code fails to save the new document.
On my dev system the document gets modified as it should, and if I break at the right place and change the right variable, the fallback code runs and generates the alternate document successfully -- but on the production server both paths fail with the same error.
Barring any better answers here, my next steps are to examine and use OpenXML and/or DocX, but making a few changes to the existing code is far preferable to picking a new tool and starting over from scratch.
Unfortunately, Lex Li was absolutely correct, and of course, the link to the reason why is posted on a site my company considers off limits, thus never showed up in my google searches prior to coding this out.
None of the tools I tried were able to handle the form I was trying to automate either -- I needed to fill in named fields and check/uncheck checkboxes, abilities which seemed beyond (or terribly convoluted in) the tools I evaluated ...
Eventually I dug into the document.xml format myself; I developed a function to modify the XML to check a named checkbox, and manipulated the raw document.xml to replace text fields with *-delimited token names. This reduced all of the necessary changes to simple string manipulation -- the rest was trivial.
The tool is 100% home-grown, not dependent upon any non-System libraries and works 100% for this particular form. It is not a generic solution by any stretch, and I suspect the document.xml file will need manual changes if and when the document is ever revised.
But for this particular problem -- it is a solution.
This was the closest I got to a complicated part. This function will check (but not uncheck) a named checkbox from a document.xml if the given condition is true.
Private Shared Function markCheckbox(xmlString As String, cbName As String, checkValue As Boolean) As String
markCheckbox = xmlString
If checkValue Then ' Checkbox needs to be checked, proceed
Dim pos As Integer = markCheckbox.IndexOf("<w:ffData><w:name w:val=""" & cbName & """/>")
If pos > -1 Then ' We have a checkbox
Dim endPos As Integer = markCheckbox.IndexOf("</w:ffData>", pos+1)
Dim cbEnd As Integer = markCheckbox.IndexOf("</w:checkBox>", pos+1)
If endPos > cbEnd AndAlso cbEnd > -1 Then ' Have found the appropriate w:ffData element (pos -> endPos) and the included insert point (cbEnd)
markCheckbox = markCheckbox.Substring(0, cbEnd) & "<w:checked/>" & markCheckbox.Substring(cbEnd)
End If
' Any other logic conditions, return the original XML string unmangled.
End If
End If
End Function

Why GetFiles from a remote folder using Linq is hanging

I'm trying to fetch the files inside a folder ordered by LastWriteTime.
The code is running very fast when accessing to a local path (C:\MyFolder), but is hanging when accessing to a remote path (\\MyServer\MyFolder)
Dim myOrderedList As List(Of String) = (From item In IO.Directory.GetFiles(strFolderSource) _
Let file = New IO.FileInfo(item) _
Order By file.LastWriteTime _
Select item).ToList()
Should this code work? Is not allowed this method to get files from a remote folder?
Which alternative code could I use to get the same result without hanging?
EDITED (2019-01-18 16:32):
Sorry guys, I've tried the proposed solution from Rango, and still the same hang. Finally I created a small logging system to catch the step that caused the problem, and realized that all is a credential problem.
Just before the code I posted I do a NET USE to grant access to the remote computer, and the net use is executed, but for any reason, the GetFiles() fails because of Logon failure: unknown user name or bad password.
So, Could I ensure the credentials with the net use before call the GetFiles()?
Maybe using a pause or something like this?
FULL CODE:
Dim processInfo As New System.Diagnostics.ProcessStartInfo()
processInfo.FileName = "C:\WINDOWS\system32\net"
processInfo.Arguments = "net use \\MyServer\IPC$ ""password"" /USER:Username"
System.Diagnostics.Process.Start(processInfo)
Dim myOrderedList As List(Of String) = (From item In IO.Directory.GetFiles("\\MyServer\g$\MyFolder") _
Let file = New IO.FileInfo(item) _
Order By file.LastWriteTime _
Select item).ToList()
You could try to use DirectoryInfo.EnumerateFiles instead wich has two advantages:
No consecutive security handshakes from the remote server necessary
Streaming the files instead of loading all into memory before you start ordering them
Dim di = new DirectoryInfo(strFolderSource)
Dim files = From fi In di.EnumerateFiles() Order By fi.LastWriteTime Select fi.FullName
Dim myOrderedList As List(Of String) = files.ToList()
Finally solved including a sleep of 5 seconds after the net use and before the GetFiles():
System.Threading.Thread.Sleep(5000)
Thanks for your time, and hope this helps anybody with a similar problem.

ManagementException: Not found

I have a program which runs on some wearable terminals in our warehouse. These terminals connect via remote desktop to our server which actually hosts and runs this program.
This program takes a scan of product, launches another program (if it isn't already running for the current user) which handles some processing, then does some additional processing once finished. One issue I'm running into is checking if the second program is already running. I've tried a lot of different code but this is what I settled on as it seemed to work perfectly fine.
Private Function CheckPrintLabelRunning() As Boolean
Dim selectQuery As String = "Select * From Win32_Process Where Name = 'vbPrintLabel.exe'"
Dim searcher As New System.Management.ManagementObjectSearcher(selectQuery)
Dim processList As System.Management.ManagementObjectCollection = searcher.[Get]()
For Each obj As System.Management.ManagementObject In processList
Dim argList As String() = New String() {String.Empty, String.Empty}
Dim returnVal As Integer = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList))
If returnVal = 0 Then
If argList(0).ToString.ToUpper = Environment.UserName.ToUpper Then
Return True
End If
End If
Next
Return False
End Function
On Thursday, while the users were using the application on the terminals, the following error message popped up in my error logs. This application has seemingly been closing itself down, hitting the main form's FormClosing event (indicating that it isn't a full crash, but something that is handled inappropriately or an environment crash perhaps?). Here is the exception:
System.Management.ManagementException: Not found
at System.Management.ManagementException.ThrowWithExtendedInfo(ManagementStatus errorCode)
at System.Management.ManagementObject.InvokeMethod(String methodName, ManagementBaseObject inParameters, InvokeMethodOptions options)
at System.Management.ManagementObject.InvokeMethod(String methodName, Object[] args)
at vbFullCasePicking.frmFullCasePicking.CheckPrintLabelRunning()
at vbFullCasePicking.frmFullCasePicking.StartPrintLabelListener()
at vbFullCasePicking.frmFullCasePicking.PrintLabel(Boolean blnSMPLabels, String strCartonID)
I would like to point out that in this particular issue, the application did not close itself down, but I am still curious about this error as it's the first I've seen it of all the months this has been in use.
EDIT: More info...the server the application runs on is Windows Server 2003. This issue has not generated any event log entries of any kind as far as we can see.
EDIT 2: I completely forgot about this, but I have a previous question regarding this issue. I was initially querying all the processes which seemed to cause errors when multiple users would try to query at the same time. This is likely the same issue, then, just with a much smaller window. Any input is greatly appreciated.

Retrieve Lotus Notes Domino directoy groups and users

Using Studio 2013 VB.
I am attempting to retrieve group members from our Lotus Notes Domino directory - but I cannot get past this error: "A protocol error occurred. Failed, invalid authentication method specified." I was assuming (maybe incorrectly) that this could be done using DirectorySearcher as we do for our Active Directory.
I have tried retrieving various data with the same results. My research seems to indicate a problem with the ldapsettings but I am using the same alias and specific ldapsettings used by other in-house scripts (albeit written in perl). So the ldapsettings still might be the problem.
The line of code that fails is:
Dim result As SearchResult = searcher.FindOne
The value of searcher.Filter is (&(objectclass=dominoGroup)(cn=mydominogroup))
So this looks like it is build right.
Any help with errors in my code - or even suggestions to accomplish this task a better way are appreciated.
Here is my code:
dim grp as String = "mydominogroup"
Using dEntry As New DirectoryEntry("LDAP://mycompanyldapsettings")
dEntry.Username = myadminaccount
dEntry.Password = myadminpassword
Using searcher As New DirectorySearcher(dEntry)
searcher.Filter = String.Format("(&(objectclass=dominoGroup)(cn={0}))", grp)
Dim result As SearchResult = searcher.FindOne <--fails here
If result Is Nothing Then
"report group not found"
Else
Dim members As Object = result.GetDirectoryEntry.Invoke("Members", Nothing)
If members Is Nothing Then
"report no members found in group"
Else
For Each member As Object In CType(members, IEnumerable)
Dim currentMember As New DirectoryEntry(member)
If currentMember.SchemaClassName.ToLower = "user" Then
Dim props As PropertyCollection = currentMember.Properties
"get and list the user pros("someattribute").Value)"
End If
Next
End If
End If
End Using
End Using
Decided to call an external Process to solve this.