1

Looking at this Stack Overflow question which references this MSDN Doc, it appears that I can't pass a parameter being sent into the calling Sub int a Lambda expression because it is a ByRef parameter.

I have created a temp parameter (two actually - there are two that are required in the Lambda expression) and passed that into the long-running Task.

The task executes very well. It uses a Parallel.ForEach expression which runs at 30% of the original time.

The issue is that both of these ByRef parameters are needed below this call.

The Parallel.ForEach inserts hundreds of values into Excel. The statements below should do formatting and also enable each cell to be clickable to open a dialog window with additional information. Neither the formatting or clickability work.

Here is the calling Sub signature, the Lambda expression, and some code that gets called afterward:

    Friend Sub RefreshFormattingSummaryReport(ByRef oDPWorksheet As CDPWorksheet, ByRef oSheet As Microsoft.Office.Interop.Excel.Worksheet,
                                          ByRef worker As BackgroundWorker)



    Dim tempDPWorksheet As New CDPWorksheet
    tempDPWorksheet = oDPWorksheet
    Dim tempOsheet As New Microsoft.Office.Interop.Excel.Worksheet
    tempOsheet = oSheet


                Parallel.ForEach(rangeList,
                Sub(singleTuple)
                    Dim iRow1 As Integer = 0
                    Dim iRow2 As Integer = 0
                    Dim iMaxRow As Integer = 0
                    Dim iCol1 As Integer = 0
                    Dim iCol2 As Integer = 0
                    Dim oRange As Range
                    Dim oRange2 As Range
                    Dim oCellValues() As String = Nothing
                    Dim oDesignSheet As Microsoft.Office.Interop.Excel.Worksheet
                    Dim bLoadFormulas As Boolean = False

                    Dim NamedRange = singleTuple.NamedRange
                    Dim NamedRangeRow = singleTuple.NamedRangeRow
                    Dim NamedRangeColumn = singleTuple.NamedRangeColumn
                    Dim NamedRangeCellValue = singleTuple.NamedRangeCellValue

                    If InStr(NamedRange, tempDPWorksheet.SheetID) > 0 Then

                        iRow1 = Integer.Parse(NamedRangeRow.ToString.Substring(0, IIf(InStr(NamedRangeRow, "-") > 0, InStr(NamedRangeRow, "-") - 1, Len(NamedRangeRow))))
                        iRow2 = Integer.Parse(NamedRangeRow.ToString.Substring(IIf(InStr(NamedRangeRow, "-") > 0, InStr(NamedRangeRow, "-"), 0), IIf(InStr(NamedRangeRow, "-") > 0, Len(NamedRangeRow) - InStr(NamedRangeRow, "-"), Len(NamedRangeRow))))
                        If iRow2 > iMaxRow Then iMaxRow = iRow2

                        If InStr(NamedRangeColumn, "DP_COL") > 0 Then
                            oRange = tempOsheet.Range(NamedRangeColumn)
                            iCol1 = oRange.Column
                            iCol2 = iCol1
                            oRange = Nothing
                        Else
                            iCol1 = Integer.Parse(NamedRangeColumn.ToString.Substring(0, IIf(InStr(NamedRangeColumn, "-") > 0, InStr(NamedRangeColumn, "-") - 1, Len(NamedRangeColumn))))
                            iCol2 = Integer.Parse(NamedRangeColumn.ToString.Substring(IIf(InStr(NamedRangeColumn, "-") > 0, InStr(NamedRangeColumn, "-"), 0), IIf(InStr(NamedRangeColumn, "-") > 0, Len(NamedRangeColumn) - InStr(NamedRangeColumn, "-"), Len(NamedRangeColumn))))
                        End If

                        oRange = CType(tempOsheet.Range(tempOsheet.Cells(iRow1, iCol1), tempOsheet.Cells(iRow2, iCol2)), Microsoft.Office.Interop.Excel.Range)
                        oCellValues = Split(Replace(Replace(Replace(Replace(Replace(Replace(NamedRangeCellValue, "{", ""), "}", ""), "&amp;", "&"), "amp;", ""), "&lt;", "<"), "&gt;", ">"), ",| ")

                        If oRange IsNot Nothing Then
                            If oCellValues.Length = oRange.Rows.Count Then

                                For j = 1 To oRange.Rows.Count

                                    Try
                                        oRange2 = oRange.Cells.Item(j)
                                        If oCellValues(j - 1).Length >= 2 Then
                                            oRange2.Value = IIf(oCellValues(j - 1).Substring(0, 2).Equals("  "), oCellValues(j - 1), LTrim(oCellValues(j - 1)))
                                        Else
                                            oRange2.Value = oCellValues(j - 1)
                                        End If
                                    Catch ex As Exception
                                        'MessageBox.Show(String.Format("Error: {0}", ex.Message, oRange.Cells.Item(j)))
                                    End Try

                                Next

                            End If
                        End If

                        tempOsheet.Names.Add(Replace(NamedRange, tempDPWorksheet.SheetID & ".", "").Replace(" ", "_").Replace("-", "_").Replace(",", ""), oRange)

                        Try


                            If tempDPWorksheet.Columns.Contains(NamedRangeColumn) Then
                                bLoadFormulas = (tempDPWorksheet.Columns.IndexOf(NamedRangeColumn) <> -1) AndAlso (DirectCast(tempDPWorksheet.Columns(tempDPWorksheet.Columns.IndexOf(NamedRangeColumn)), CColumn).Type = EColumnTypes.Excel) Or NameIsExcelRow(NamedRange.ToString)
                            Else
                                bLoadFormulas = False
                            End If

                            If Not (InStr(NamedRange, "DP_FRO") > 0 Or InStr(NamedRange, "DRILLPOINT") > 0) Then
                                ' ************** Repetitive Copy/Paste operations
                                RangeCopyDesignFormat(oRange, oDesignSheet.Range(Replace(NamedRange, tempDPWorksheet.SheetID & ".", "")), False, Not tempDPWorksheet.IncludeFormatting Or Not (Not (InStr(NamedRange, "SKIP") > 0) Or tempDPWorksheet.FormatSkipped), False, "", bLoadformulas:=bLoadFormulas, bLoadComments:=True)
                            End If

                        Catch ex As Exception

                        End Try

                    End If
                End Sub)

And some calls that are made afterward:

    oSheet = tempOsheet
    oDPWorksheet = tempDPWorksheet

    ReplaceKeywordsInHeaderAndFooter("", oDBInfo, oSheet, "", "", oProp.Value)

    If oDPWorksheet.IncludeDrilldown Then
        DrillDownLoad(oSheet, oDPWorksheet, oSmartTags, oDBInfo, "", 4 + iRowOffset)
    End If

    SuppressRows(oSheet, oDPWorksheet, oDBInfo, iMaxRow, iRowOffset:=iRowOffset)

    If Not oDPWorksheet.DoNotAutofitColumns Then
        tWidth = oSheet.Range("DP_FRO_" & oDPWorksheet.FROutlineID).ColumnWidth
        oSheet.Range("DRILLPOINT").Columns.AutoFit()
        oSheet.Range("DP_FRO_" & oDPWorksheet.FROutlineID).ColumnWidth = tWidth
    End If
    HideColumns(oSheet, oDPWorksheet.Columns, "DP_FRO_" & oDPWorksheet.FROutlineID, oDPWorksheet.HideFROColumn)

I have tried assigning the temp variables back to the ByRef parameters, and I have also tried changing each instance of the ByRef parameters in the rest of the Sub with the temp names. Neither work.

Any thoughts? Any suggestions are appreciated.

  • Which parameters are `ByRef`? Why are they `ByRef`? Can you show the method signature? – Chris Dunaway Feb 28 '19 at 20:48
  • @ChrisDunaway - Signature added to code. The worksheet and sheet objects are passed around to several other Subs – Jason P Sallinger Feb 28 '19 at 20:55
  • You don't appear to be reassigning a new worksheet to either `oDPWorksheet` or `oSheet` and I can't imagine a scenario where you would reassign a new background worker to the `worker` variable. It seems like these variables don't need to be passed `ByRef` at all. – Chris Dunaway Feb 28 '19 at 21:06
  • Ok. Maybe this is assignment overkill. This is legacy code from ~2005. I will test it by removing all the ByRef's in the Sub calls. Thanks. – Jason P Sallinger Feb 28 '19 at 21:24
  • Removed the ByRef. Good thing is, nothing happened. Bad thing is, nothing happened. Still don't get the formatting or clickability. I might reassess and open a question with a different problem statement. – Jason P Sallinger Feb 28 '19 at 21:46
  • Attempting to access Excel from multiple threads is doable, but its correct implementation involves registering a COM IMesageFilter and the potential gains will be minimal if each thread is mainly interacting with Excel. Add in the overhead of marshaling from MTA (parallel thread) to STA apartment also diminishes the benefit. If you doubt my statements, please see [this forum thread and the post from Geoff Darst](https://social.msdn.microsoft.com/Forums/vstudio/en-US/f38edd9b-34dd-4cc0-bad2-71d42b667477/multithreading-in-vsto?forum=vsto). – TnTinMn Feb 28 '19 at 22:39
  • Awesome. Thanks for this feedback. I will read up on this tomorrow. – Jason P Sallinger Mar 01 '19 at 02:00

0 Answers0