diff --git a/SQL2012_BIDSHelper.csproj b/SQL2012_BIDSHelper.csproj index 5553c6d..d94ee01 100644 --- a/SQL2012_BIDSHelper.csproj +++ b/SQL2012_BIDSHelper.csproj @@ -23,7 +23,7 @@ true BIDSHelper.snk - v4.0 + v4.5 publish\ true Disk @@ -54,8 +54,11 @@ full true true - ..\..\..\Program Files (x86)\Microsoft Visual Studio 11.0\Team Tools\Static Analysis Tools\Rule Sets\BIDSHelper.ruleset + BasicCorrectnessRules.ruleset false + x86 + false + false false @@ -70,6 +73,7 @@ 1591 AllRules.ruleset true + false @@ -87,6 +91,7 @@ full AnyCPU AllRules.ruleset + false @@ -110,6 +115,7 @@ True + False $(ProgramFiles)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Business Intelligence Semantic Model\Microsoft.AnalysisServices.BackEnd.dll @@ -129,16 +135,18 @@ c:\Program Files\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\Microsoft.AnalysisServices.Design.DLL False - - $(ProgramFiles)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Business Intelligence Semantic Model\Microsoft.AnalysisServices.MPFProjectBase.dll + + False + ..\..\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PrivateAssemblies\Business Intelligence Semantic Model\Microsoft.AnalysisServices.MPFProjectBase.11.dll False True False - - $(ProgramFiles)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Business Intelligence Semantic Model\Microsoft.AnalysisServices.VSHost.dll + + False + ..\..\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PrivateAssemblies\Business Intelligence Semantic Model\Microsoft.AnalysisServices.VSHost.11.dll False @@ -161,6 +169,7 @@ True + False True @@ -169,9 +178,11 @@ ..\..\..\Windows\assembly\GAC_MSIL\Microsoft.ReportViewer.Common\11.0.0.0__89845dcd8080cc91\Microsoft.ReportViewer.Common.dll + False ..\..\..\Windows\assembly\GAC_MSIL\Microsoft.ReportViewer.WinForms\11.0.0.0__89845dcd8080cc91\Microsoft.ReportViewer.WinForms.DLL + False True @@ -179,15 +190,17 @@ True + False True False - + False False - ..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SqlServer.ExecPackageTaskWrap\v4.0_12.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ExecPackageTaskWrap.dll + ..\..\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.SqlServer.ExecPackageTaskWrap\v4.0_11.0.0.0__89845dcd8080cc91\Microsoft.SqlServer.ExecPackageTaskWrap.dll + False False @@ -204,19 +217,25 @@ True + False False ..\..\Program Files (x86)\Microsoft SQL Server\110\DTS\Tasks\Microsoft.SqlServer.SQLTask.dll + False True + False False + False + + + False - True DLLs\SQL2012\msddsp.dll @@ -265,6 +284,7 @@ True + False @@ -509,12 +529,6 @@ - - Form - - - FindReferences.cs - UserControl @@ -527,9 +541,18 @@ FindUnusedVariables.cs + + Form + + + FindVariableReferences.cs + + + Code + @@ -972,9 +995,6 @@ DesignWarningsOptionsPage.cs Designer - - FindReferences.cs - FindReferencesControl.cs Designer @@ -982,6 +1002,9 @@ FindUnusedVariables.cs + + FindVariableReferences.cs + ExpressionListControl.cs diff --git a/SQL2012_BIDSHelper.csproj.user b/SQL2012_BIDSHelper.csproj.user index 86ec0d8..9d8dfd6 100644 --- a/SQL2012_BIDSHelper.csproj.user +++ b/SQL2012_BIDSHelper.csproj.user @@ -1,7 +1,7 @@  - ProjectFiles + ShowAllFiles C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PrivateAssemblies\;C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\;C:\Program Files (x86)\Microsoft SQL Server\110\SDK\Assemblies\;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\;C:\Program Files (x86)\Microsoft SQL Server\100\Tools\Binn\VSShell\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\PrivateAssemblies\Business Intelligence Semantic Model\ @@ -9,6 +9,6 @@ Program C:\Program Files %28x86%29\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe - /resetaddin BIDSHelper2012-ForTesting.addin + "C:\Users\greendg\Documents\Visual Studio 2012\Projects\SSISPlay\SSISPlay.sln" \ No newline at end of file diff --git a/SQL2014_BIDSHelper.csproj b/SQL2014_BIDSHelper.csproj index be7c1f5..b42b08c 100644 --- a/SQL2014_BIDSHelper.csproj +++ b/SQL2014_BIDSHelper.csproj @@ -503,17 +503,11 @@ - + Form - - FindReferences.cs - - - UserControl - - - FindReferencesControl.cs + + FindVariableReferences.cs Form @@ -967,11 +961,8 @@ DesignWarningsOptionsPage.cs Designer - - FindReferences.cs - - - FindReferencesControl.cs + + FindVariableReferences.cs Designer diff --git a/SSIS/Biml/BimlUtility.cs b/SSIS/Biml/BimlUtility.cs index d17c82a..e19051c 100644 --- a/SSIS/Biml/BimlUtility.cs +++ b/SSIS/Biml/BimlUtility.cs @@ -113,14 +113,26 @@ private static string GetValidationReporterMessage(ValidationItem item) builder.AppendFormat("Schema {0}. ", item.SchemaName); } - if (item.Exception != null) + ParseException(item.Exception, ref builder); + + return builder.ToString().TrimEnd(); + } + + private static void ParseException(System.Exception exception, ref StringBuilder builder) + { + if (exception == null) { - // Just show the exception type name, which can help indicate the type of issue, e.g. invalid XML vs invalid BIML - // We don't want the full Exception as it is confusing and makes you think something has gone wrong. - builder.AppendFormat("Exception type: {0}", item.Exception.GetType().Name); + return; } - return builder.ToString().TrimEnd(); + // Changed my mind!!! + //// Just show the exception type name, which can help indicate the type of issue, e.g. invalid XML vs invalid BIML + //// We don't want the full Exception as it is confusing and makes you think something has gone wrong. + //builder.AppendFormat("Exception type: {0}", item.Exception.GetType().Name); + + builder.AppendFormat("\tException type: {0}, Message: {1}\r\n", exception.GetType().Name, exception.Message); + + ParseException(exception.InnerException, ref builder); } private static void GetMessageString(string input, ref StringBuilder builder) diff --git a/SSIS/ComponentInfo.cs b/SSIS/ComponentInfo.cs index b5f96ff..f42a916 100644 --- a/SSIS/ComponentInfo.cs +++ b/SSIS/ComponentInfo.cs @@ -16,46 +16,65 @@ public class ComponentInfo private static unsafe extern int DestroyIcon(IntPtr hIcon); private DTSPipelineComponentType componentType; - private string id; - private string name; - private string creationName; - private Icon icon; public ComponentInfo(PipelineComponentInfo componentInfo) { this.componentType = componentInfo.ComponentType; - this.id = componentInfo.ID; - this.name = componentInfo.Name; - this.creationName = componentInfo.CreationName; + this.ID = componentInfo.ID; + this.Name = componentInfo.Name; + this.CreationName = componentInfo.CreationName; + + Assembly assembly = GetComponentAssembly(componentInfo); + + if (assembly != null) + { + Stream iconStream = assembly.GetManifestResourceStream(componentInfo.IconResource); + if (iconStream != null) + { + this.Icon = new Icon(iconStream, new Size(16, 16)); + } + } + else + { + int index = 0; + Int32.TryParse(componentInfo.IconResource, out index); + this.Icon = ExtractIcon(componentInfo.IconFile, index, false); + } + + // Ensure we always have an icon + if (this.Icon == null) + { + this.Icon = BIDSHelper.Resources.Common.NoIcon; + } } public ComponentInfo(TaskInfo componentInfo) { - this.id = componentInfo.ID; - this.name = componentInfo.Name; - this.creationName = componentInfo.CreationName; + this.ID = componentInfo.ID; + this.Name = componentInfo.Name; + this.CreationName = componentInfo.CreationName; - Assembly assembly = ComponentInfo.GetComponentAssembly(componentInfo); + Assembly assembly = GetComponentAssembly(componentInfo); if (assembly != null) { Stream iconStream = assembly.GetManifestResourceStream(componentInfo.IconResource); if (iconStream != null) { - this.icon = new Icon(iconStream, new Size(16, 16)); + this.Icon = new Icon(iconStream, new Size(16, 16)); } } else { int index = 0; Int32.TryParse(componentInfo.IconResource, out index); - this.icon = ExtractIcon(componentInfo.IconFile, index, false); + this.Icon = ExtractIcon(componentInfo.IconFile, index, false); } // Ensure we always have an icon - if (this.icon == null) + if (this.Icon == null) { - this.icon = BIDSHelper.Resources.Common.NoIcon; + this.Icon = BIDSHelper.Resources.Common.NoIcon; } } @@ -68,11 +87,11 @@ public ComponentInfo(Icon icon) { if (icon.Height > 16) { - this.icon = new Icon(icon, new Size(16, 16)); + this.Icon = new Icon(icon, new Size(16, 16)); } else { - this.icon = icon; + this.Icon = icon; } } @@ -154,27 +173,27 @@ private static Icon ExtractIcon(string file, int index, bool large) public DTSPipelineComponentType ComponentType { - get { return this.componentType; } + get; private set; } public string ID { - get { return this.id; } + get; private set; } public string Name { - get { return this.name; } + get; private set; } public string CreationName { - get { return this.creationName; } + get; private set; } public Icon Icon { - get { return this.icon; } + get; private set; } } } diff --git a/SSIS/FindUnusedVariables.cs b/SSIS/FindUnusedVariables.cs index 9ebac1f..a71c43d 100644 --- a/SSIS/FindUnusedVariables.cs +++ b/SSIS/FindUnusedVariables.cs @@ -127,7 +127,7 @@ private void processPackage_DoWork(object sender, DoWorkEventArgs e) stopwatch.Start(); Variable[] variables = e.Argument as Variable[]; - finder.FindReferences(this.package, variables); + finder.FindReferences(this.package, variables, null); } #endregion diff --git a/SSIS/FindVariables.cs b/SSIS/FindVariables.cs index e88d6f1..b953592 100644 --- a/SSIS/FindVariables.cs +++ b/SSIS/FindVariables.cs @@ -1,15 +1,29 @@ -using Microsoft.SqlServer.Dts.Pipeline.Wrapper; +using Microsoft.DataTransformationServices.Design; +using Microsoft.SqlServer.Dts.Pipeline.Wrapper; using Microsoft.SqlServer.Dts.Runtime; using Microsoft.SqlServer.Dts.Tasks.ExecutePackageTask; using Microsoft.SqlServer.Dts.Tasks.ExecuteSQLTask; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Drawing; +using System.Windows.Forms; namespace BIDSHelper.SSIS { internal class FindVariables { + public const string IconKeyFolder = "Folder"; + public const string IconKeyProperties = "Properties"; + public const string IconKeyInput = "Input"; + public const string IconKeyOutput = "Output"; + public const string IconKeyColumn = "Column"; + public const string IconKeyPrecedenceConstraint = "PrecedenceConstraint"; + public const string IconKeyVariable = "Variable"; + public const string IconKeyVariableExpression = "VariableExpression"; + public const string IconKeyProperty = "Property"; + public const string IconKeyPropertyExpression = "PropertyExpression"; + private string[] expressionMatches = null; private string[] properytMatches = null; @@ -18,6 +32,7 @@ internal class FindVariables private const string ObjectTypeDataFlowTask = "DataFlowTask"; private const string ObjectTypeExecutePackageTask = "ExecutePackageTask"; + private TreeView treeView; public event EventHandler VariableFound; @@ -34,12 +49,12 @@ public bool Cancel() return true; } - public void FindReferences(Package package, Variable variable) + public void FindReferences(Package package, Variable variable, TreeView treeView) { - FindReferences(package, new Variable[] { variable }); + FindReferences(package, new Variable[] { variable }, treeView); } - public void FindReferences(Package package, Variable[] variables) + public void FindReferences(Package package, Variable[] variables, TreeView treeView) { // Reset cancel pending flag this.CancellationPending = false; @@ -53,7 +68,7 @@ public void FindReferences(Package package, Variable[] variables) expressions.Add(string.Format("@{0}", variable.Name)); expressions.Add(string.Format("@[{0}]", variable.Name)); expressions.Add(string.Format("@[{0}]", variable.QualifiedName)); - + // Clean qualified name for property match properties.Add(variable.QualifiedName); } @@ -62,58 +77,257 @@ public void FindReferences(Package package, Variable[] variables) this.expressionMatches = expressions.ToArray(); this.properytMatches = properties.ToArray(); - ProcessObject(package, string.Empty); + this.treeView = treeView; + + if (treeView != null) + { + SetupImageList(); + + // Create package node, the top level node + int imageIndex = GetControlFlowImageIndex(PackageHelper.PackageCreationName); + TreeNode packageNode = new TreeNode(package.Name, imageIndex, imageIndex); + packageNode.Tag = package; + AddRootNode(packageNode); + + ProcessObject(package, packageNode); + } + else + { + ProcessObject(package, null); + } + // Reset cancel pending in case we have cancelled, as about to exit, so no longer pending this.CancellationPending = false; } - private void ProcessObject(object component, string path) + delegate void AddRootNodeCallback(TreeNode node); + + private void AddRootNode(TreeNode node) + { + if (node == null) + throw new ArgumentNullException("node"); + + if (this.treeView.InvokeRequired) + { + AddRootNodeCallback callback = new AddRootNodeCallback(AddRootNode); + this.treeView.Invoke(callback, new object[] { node }); + } + else + { + this.treeView.Nodes.Add(node); + } + } + + delegate void AddNodeCallback(TreeNode treeView, TreeNode node); + + private TreeNode AddNode(TreeNode parentNode, string text, int imageIndex, object tag) + { + return AddNode(parentNode, text, imageIndex, tag, false); + } + + private TreeNode AddNode(TreeNode parentNode, string text, int imageIndex, object tag, bool found) + { + if (parentNode == null) + return null; + + TreeNode node = new TreeNode(text, imageIndex, imageIndex); + node.Name = text; + node.Tag = tag; + node.Checked = found; + + AddNodeSafe(parentNode, node); + + return node; + } + + + private void AddNodeSafe(TreeNode parentNode, TreeNode childNode) + { + if (parentNode == null) + return; + + if (childNode == null) + return; + + if (treeView.InvokeRequired) + { + AddNodeCallback callback = new AddNodeCallback(AddNodeSafe); + treeView.Invoke(callback, new object[] { parentNode, childNode }); + } + else + { + parentNode.Nodes.Add(childNode); + + if (childNode.Checked) + { + TreeNode target = childNode; + while (target != null) + { + target.Expand(); + + target = target.Parent; + } + } + } + } + + private int GetControlFlowImageIndex(string creationName) + { + if (treeView == null) + return -1; + + int imageIndex = treeView.ImageList.Images.IndexOfKey(creationName); + if (imageIndex == -1) + { + AddImageListItem(creationName, PackageHelper.ControlFlowInfos[creationName].Icon); + imageIndex = treeView.ImageList.Images.Count - 1; + } + + return imageIndex; + } + + private int GetComponentImageIndex(string creationName) + { + if (treeView == null) + return -1; + + int imageIndex = treeView.ImageList.Images.IndexOfKey(creationName); + if (imageIndex == -1) + { + AddImageListItem(creationName, PackageHelper.ComponentInfos[creationName].Icon); + imageIndex = treeView.ImageList.Images.Count - 1; + } + + return imageIndex; + } + + private int GetImageIndex(string iconKey) + { + if (treeView == null) + return -1; + + return treeView.ImageList.Images.IndexOfKey(iconKey); + } + + private void SetupImageList() + { + // Add some standard icons to our image list. Order is fixed, since we have hardcoded index values in GetImageIndex function + AddImageListItem(IconKeyFolder, SharedIcons.FolderOpened); + AddImageListItem(IconKeyProperties, SharedIcons.AllProperties); + AddImageListItem(IconKeyInput, SharedIcons.FolderOpened); + AddImageListItem(IconKeyOutput, SharedIcons.FolderOpened); + AddImageListItem(IconKeyColumn, SharedIcons.FolderOpened); + AddImageListItem(IconKeyPrecedenceConstraint, SharedIcons.PrecedenceConstraint); + AddImageListItem(IconKeyVariable, SharedIcons.Variable_properties); + AddImageListItem(IconKeyVariableExpression, SharedIcons.VariableExpressionIcon); + AddImageListItem(IconKeyProperty, BIDSHelper.Resources.Versioned.Variable); + AddImageListItem(IconKeyPropertyExpression, SharedIcons.VariableExpressionIcon); + } + + delegate void AddImageListItemCallback(string creationName, Icon image); + + private void AddImageListItem(string creationName, Icon image) + { + if (image == null) + return; + + if (treeView.InvokeRequired) + { + AddImageListItemCallback callback = new AddImageListItemCallback(AddImageListItem); + treeView.Invoke(callback, new object[] { creationName, image }); + } + else + { + this.treeView.ImageList.Images.Add(creationName, image); + } + } + + private void ProcessObject(object component, TreeNode parentNode) { if (this.CancellationPending) { return; } - DtsContainer container = component as DtsContainer; - // Should only get package as we call GetPackage up front. Could make scope like, but need UI indicator that this is happening Package package = component as Package; if (package != null) { - path = "\\Package"; - CheckConnectionManagers(package, path); - } - else if (!(component is DtsEventHandler)) - { - path = path + "\\" + container.Name; + // Package node is created in calling function. + CheckConnectionManagers(package, parentNode); } - IDTSPropertiesProvider propertiesProvider = component as IDTSPropertiesProvider; - if (propertiesProvider != null) + DtsContainer container = component as DtsContainer; + if (container != null) { - CheckProperties(propertiesProvider, path); + string containerKey = PackageHelper.GetContainerKey(container); + TaskHost taskHost = null; + + if (package == null) + { + int imageIndex = GetControlFlowImageIndex(container.CreationName); + parentNode = AddNode(parentNode, container.Name, imageIndex, component); + taskHost = container as TaskHost; + } + + if (taskHost != null) + { + CheckTask(taskHost, parentNode); + } + else if (containerKey == PackageHelper.ForLoopCreationName) + { + CheckForLoop(container as IDTSPropertiesProvider, parentNode); + } + else if (containerKey == PackageHelper.ForEachLoopCreationName) + { + CheckForEachLoop(container as ForEachLoop, parentNode); + } + else if (containerKey == PackageHelper.SequenceCreationName) + { + ScanProperties(container as IDTSPropertiesProvider, parentNode); + } + else + { + ScanProperties(container as IDTSPropertiesProvider, parentNode); + } + + string currentPath = string.Empty; + IDTSPackagePath packagePath = component as IDTSPackagePath; + if (packagePath != null) + { + currentPath = packagePath.GetPackagePath(); + } + + ScanVariables(container.Variables, parentNode, currentPath); } EventsProvider eventsProvider = component as EventsProvider; if (eventsProvider != null) { + TreeNode eventHandlers = AddFolder("EventHandlers", parentNode); + int imageIndex = GetControlFlowImageIndex(PackageHelper.EventHandlerCreationName); + foreach (DtsEventHandler eventhandler in eventsProvider.EventHandlers) { - ProcessObject(eventhandler, path + ".EventHandlers[" + eventhandler.Name + "]"); + TreeNode node = AddNode(parentNode, eventhandler.Name, imageIndex, eventhandler); + ProcessObject(eventhandler, node); } } IDTSSequence sequence = component as IDTSSequence; if (sequence != null) { - ProcessSequence(container, sequence, path); - ScanPrecedenceConstraints(path, container.ID, sequence.PrecedenceConstraints); + ProcessSequence(sequence, parentNode); + ScanPrecedenceConstraints(container.ID, sequence.PrecedenceConstraints, parentNode); } } - private void ProcessSequence(DtsContainer container, IDTSSequence sequence, string path) + private void ProcessSequence(IDTSSequence sequence, TreeNode parentNode) { + if (sequence == null) + return; + if (this.CancellationPending) { return; @@ -121,117 +335,72 @@ private void ProcessSequence(DtsContainer container, IDTSSequence sequence, stri foreach (Executable executable in sequence.Executables) { - ProcessObject(executable, path); + ProcessObject(executable, parentNode); } } - private void CheckConnectionManagers(Package package, string path) + private void CheckConnectionManagers(Package package, TreeNode parentNode) { if (this.CancellationPending) return; + TreeNode folder = AddFolder("Connections", parentNode); + + int imageIndex = GetControlFlowImageIndex(PackageHelper.ConnectionCreationName); + foreach (ConnectionManager connection in package.Connections) { DtsContainer container = (DtsContainer)package; - // TODO; Fix - Cheat and hard code creation name as icon routines cannot get the correct connection icon - - VariableFoundEventArgs foundArgument = new VariableFoundEventArgs(); - foundArgument.ContainerID = container.ID; - foundArgument.ObjectID = connection.ID; - foundArgument.ObjectType = connection.GetType().Name; - foundArgument.Type = typeof(ConnectionManager); - foundArgument.Icon = PackageHelper.ControlFlowInfos[PackageHelper.ConnectionCreationName].Icon; - foundArgument.ObjectPath = path + ".Connections[" + connection.Name + "]."; - foundArgument.ObjectName = connection.Name; - - //private void ScanProperties(string objectPath, Type objectType, string objectTypeName, string containerID, string objectID, string objectName, IDTSPropertiesProvider provider, string containerKey) - ScanProperties((IDTSPropertiesProvider)connection, foundArgument); + TreeNode node = AddNode(folder, connection.Name, imageIndex, connection); + ScanProperties((IDTSPropertiesProvider)connection, node); } } - private void CheckProperties(IDTSPropertiesProvider propProvider, string path) + private TreeNode AddFolder(string folder, TreeNode parentNode) { - if (this.CancellationPending) return; - - if (propProvider is DtsContainer) - { - DtsContainer container = (DtsContainer)propProvider; - - VariableFoundEventArgs foundArgument = new VariableFoundEventArgs(); - foundArgument.ContainerID = container.ID; - foundArgument.ObjectID = container.ID; - - foundArgument.ObjectPath = path; - - string containerKey = PackageHelper.GetContainerKey(container); - foundArgument.Icon = PackageHelper.ControlFlowInfos[containerKey].Icon; - - TaskHost taskHost = container as TaskHost; - if (taskHost != null) - { - CheckTask(taskHost, foundArgument); - } - else - { - foundArgument.ObjectType = container.GetType().Name; + if (parentNode == null) + return null; - switch (foundArgument.ObjectType) - { - case "ForLoop" : - CheckForLoop(propProvider, foundArgument); - break; - case "ForEachLoop": - CheckForEachLoop(container as ForEachLoop, foundArgument); - break; - default: - foundArgument.Type = container.GetType(); - ScanProperties(propProvider, foundArgument); - break; - } - } - - ScanVariables(container.Variables, foundArgument); - } + int imageIndex = treeView.ImageList.Images.IndexOfKey(IconKeyFolder); + return AddNode(parentNode, folder, imageIndex, null); } - private void CheckTask(TaskHost taskHost, VariableFoundEventArgs foundArgument) + private void CheckTask(TaskHost taskHost, TreeNode parent) { - foundArgument.Type = taskHost.InnerObject.GetType(); - string typeName = foundArgument.Type.Name; + string typeName = taskHost.InnerObject.GetType().Name; - // Set type, used in case of expression editor, if we get that far. Correct it later as required too - foundArgument.Type = taskHost.InnerObject.GetType(); + // Scan regular task properties and expressions + // We may use the TreeView Properties folder in task specific checks, so do this first + ScanProperties(taskHost, parent); - // Task specific checks, split by native and managed if (typeName == "__ComObject") { + // Translate creation name via PackageHelper, as that caters for both nice and GUID formats, e.g. For SQL 2012 a dataFLow task can be either "{5918251B-2970-45A4-AB5F-01C3C588FE5A}" or SSIS.Pipeline.3 + string creatioName = PackageHelper.ControlFlowInfos[taskHost.CreationName].CreationName; + // Native code tasks, can't use type name, so use creation name. // Need to be wary of suffix, SSIS.ExecutePackageTask.3 for 2012, SSIS.ExecutePackageTask.4 for 2014 etc - if (taskHost.CreationName == string.Format("SSIS.{0}.{1}", ObjectTypeExecutePackageTask, SSISHelpers.CreationNameIndex)) + if (creatioName == string.Format("SSIS.{0}.{1}", ObjectTypeExecutePackageTask, SSISHelpers.CreationNameIndex)) { - foundArgument.Type = typeof(ExecutePackageTask); - foundArgument.ObjectType = ObjectTypeExecutePackageTask; - CheckExecutePackageTask(taskHost, foundArgument); + //foundArgument.Type = typeof(ExecutePackageTask); + //foundArgument.ObjectType = ObjectTypeExecutePackageTask; + CheckExecutePackageTask(taskHost, parent); } - else if (taskHost.CreationName == string.Format("SSIS.Pipeline.{0}", SSISHelpers.CreationNameIndex)) + else if (creatioName == string.Format("SSIS.Pipeline.{0}", SSISHelpers.CreationNameIndex)) { - foundArgument.ObjectType = ObjectTypeDataFlowTask; - foundArgument.Type = typeof(MainPipe); + //foundArgument.ObjectType = ObjectTypeDataFlowTask; + //foundArgument.Type = typeof(MainPipe); //foundArgument.Icon = BIDSHelper.Resources.Versioned.DataFlow; MainPipe pipeline = taskHost.InnerObject as MainPipe; - ScanPipeline(pipeline, foundArgument); + ScanPipeline(pipeline, parent); } else { - foundArgument.ObjectType = "**UnknownNativeTask**"; System.Diagnostics.Debug.Assert(false, "Unrecognised native task - " + taskHost.CreationName); } } else { - // Set type name, as it is correct for managed tasks - foundArgument.ObjectType = typeName; - // For managed code tasks we can use type name. This means we don't have to have a // full reference to the task assembly, but any properties we access must be simple // ones accessible via IDTSPropertiesProvider, i.e. ExpressionTask. For more complex @@ -239,19 +408,16 @@ private void CheckTask(TaskHost taskHost, VariableFoundEventArgs foundArgument) switch (typeName) { case "ExecuteSQLTask": - CheckExecuteSQLTask(taskHost, foundArgument); + CheckExecuteSQLTask(taskHost, parent); break; case "ExpressionTask": - CheckExpressionTask(taskHost, foundArgument); + CheckExpressionTask(taskHost, parent); break; case "ExecutePackageTask": - CheckExecutePackageTask(taskHost, foundArgument); + CheckExecutePackageTask(taskHost, parent); break; } } - - // Scan regular task properties and epressions - ScanProperties(taskHost, foundArgument); } private static bool GetIsFriendlyExpression(IDTSCustomPropertyCollection100 properties) @@ -280,58 +446,61 @@ private static bool GetIsFriendlyExpression(IDTSCustomPropertyCollection100 prop return false; } - private void ScanPipeline(MainPipe pipeline, VariableFoundEventArgs foundArgument) + private void ScanPipeline(MainPipe pipeline, TreeNode parent) { + TreeNode folder = AddFolder("Components", parent); + // Careful if trying to use //Parallel.ForEach(pipeline.ComponentMetaDataCollection.OfType(), componentMetadata => - // Causes issues with twp threads using the same common foundArgument. Need to clone it first. - foreach (IDTSComponentMetaData100 componentMetadata in pipeline.ComponentMetaDataCollection) + // Causes issues with two threads using the same common foundArgument. Need to clone it first. + foreach (IDTSComponentMetaData100 componentMetaData in pipeline.ComponentMetaDataCollection) { - foundArgument.ObjectID = componentMetadata.ID.ToString(); - foundArgument.ObjectName = componentMetadata.Name; - string componentPath = foundArgument.ObjectPath + "\\" + componentMetadata.Name; - string componentKey = PackageHelper.GetComponentKey(componentMetadata); - foundArgument.ObjectType = PackageHelper.ComponentInfos[componentKey].Name; + string componentKey = PackageHelper.GetComponentKey(componentMetaData); + int imageIndex = GetComponentImageIndex(componentKey); + TreeNode componentNode = AddNode(folder, componentMetaData.Name, imageIndex, componentMetaData); ; - ScanCustomPropertiesCollection(componentMetadata.CustomPropertyCollection, foundArgument, componentPath);// containerId, objectId, objectName, componentPath, objectType); + ScanCustomPropertiesCollection(componentMetaData.CustomPropertyCollection, componentNode); #region Inputs, Outputs, Columns // Scan inputs and input columns - foreach (IDTSInput100 input in componentMetadata.InputCollection) + foreach (IDTSInput100 input in componentMetaData.InputCollection) { - string localPath = componentPath + ".Input[" + input.Name + "]"; - ScanCustomPropertiesCollection(input.CustomPropertyCollection, foundArgument, localPath); + TreeNode node = AddNode(componentNode, "Input [" + input.Name + "]", GetImageIndex(IconKeyInput), input); + ScanCustomPropertiesCollection(input.CustomPropertyCollection, node); + TreeNode columnsNode = AddFolder("Output Columns", node); foreach (IDTSInputColumn100 column in input.InputColumnCollection) { - string columnPath = localPath + ".Columns[" + column.Name + "]"; - ScanCustomPropertiesCollection(column.CustomPropertyCollection, foundArgument, localPath); + TreeNode columnNode = AddNode(columnsNode, column.Name, GetImageIndex(IconKeyColumn), column); + ScanCustomPropertiesCollection(column.CustomPropertyCollection, columnNode); } } // Scan outputs and output columns - foreach (IDTSOutput100 output in componentMetadata.OutputCollection) + foreach (IDTSOutput100 output in componentMetaData.OutputCollection) { - string localPath = componentPath + ".Output[" + output.Name + "]"; - ScanCustomPropertiesCollection(output.CustomPropertyCollection, foundArgument, localPath); + TreeNode node = AddNode(componentNode, "Output [" + output.Name + "]", GetImageIndex(IconKeyOutput), output); + ScanCustomPropertiesCollection(output.CustomPropertyCollection, componentNode); + TreeNode columnsNode = AddFolder("Output Columns", node); foreach (IDTSOutputColumn100 column in output.OutputColumnCollection) { - string columnPath = localPath + ".Columns[" + column.Name + "]"; - ScanCustomPropertiesCollection(column.CustomPropertyCollection, foundArgument, localPath); + TreeNode columnNode = AddNode(columnsNode, column.Name, GetImageIndex(IconKeyColumn), column); + ScanCustomPropertiesCollection(column.CustomPropertyCollection, columnNode); } } #endregion - #region Derived Column Transformation + // Derived Column Transformation if (componentKey == "{18E9A11B-7393-47C5-9D47-687BE04A6B09}") { // Component specific logic - TBC + // Most seems to be covered by columns, as that is where the expressions are stored. } - #endregion + } } - private void ScanCustomPropertiesCollection(IDTSCustomPropertyCollection100 properties, VariableFoundEventArgs foundArgument, string componentPath) + private void ScanCustomPropertiesCollection(IDTSCustomPropertyCollection100 properties, TreeNode parent) { // string containerId, string objectId, string objectName, string path, string objectType // First check if we have a "FriendlyExpression". We use the value from FriendlyExpression, because it is CPET_NOTIFY @@ -359,14 +528,10 @@ private void ScanCustomPropertiesCollection(IDTSCustomPropertyCollection100 prop propertyName = "Expression"; } - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = componentPath + ".Properties[" + propertyName + "]"; - info.PropertyName = propertyName; - info.Value = value; - info.IsExpression = true; - info.Icon = BIDSHelper.Resources.Versioned.DataFlow; + VariableFoundEventArgs info = new VariableFoundEventArgs(); info.Match = match; OnRaiseVariableFound(info); + AddNode(parent, propertyName, GetImageIndex(IconKeyPropertyExpression), new PropertyExpression(propertyName, value, property.Value.GetType()), true); } } else @@ -376,28 +541,28 @@ private void ScanCustomPropertiesCollection(IDTSCustomPropertyCollection100 prop continue; } - string pathOverride; - if (propertyName.StartsWith("[")) - { - pathOverride = componentPath + ".Properties" + propertyName + ""; - } - else + + if (PropertyMatch(propertyName, value, out match)) { - pathOverride = componentPath + ".Properties[" + propertyName + "]"; + VariableFoundEventArgs info = new VariableFoundEventArgs(); + info.Match = match; + OnRaiseVariableFound(info); + // new PropertyInfo( + AddNode(parent, propertyName, GetImageIndex(IconKeyProperty), property, true); } - - PropertyMatch(foundArgument, pathOverride, propertyName, value); } } } - private void ScanPrecedenceConstraints(string objectPath, string containerID, PrecedenceConstraints constraints) + private void ScanPrecedenceConstraints(string containerID, PrecedenceConstraints constraints, TreeNode parent) { if (this.CancellationPending) { return; } + TreeNode constraintsNode = AddFolder("PrecedenceConstraints", parent); + foreach (PrecedenceConstraint constraint in constraints) { // Check properties @@ -419,29 +584,24 @@ private void ScanPrecedenceConstraints(string objectPath, string containerID, Pr if (ExpressionMatch(constraint.Expression, out match)) { VariableFoundEventArgs info = new VariableFoundEventArgs(); - info.ContainerID = containerID; - info.ObjectID = constraint.ID; - info.ObjectName = ((DtsContainer)constraint.PrecedenceExecutable).Name; - info.ObjectPath = objectPath + ".PrecedenceConstraints[" + constraint.Name + "]"; - info.Type = typeof(PrecedenceConstraint); - info.ObjectType = constraint.GetType().Name; - info.PropertyName = constraint.Name; - info.Value = constraint.Expression; - info.IsExpression = true; - info.Icon = BIDSHelper.Resources.Common.Path; info.Match = match; OnRaiseVariableFound(info); + AddNode(constraintsNode, "Expression", GetImageIndex(IconKeyPrecedenceConstraint), constraint, true); + //AddNode(constraintsNode, "Expression", GetImageIndex(IconKeyPrecedenceConstraint), new PropertyExpression(propertyName, expression, PackageHelper.GetTypeFromTypeCode(property.Type)), true); } } } - private void ScanVariables(Variables variables, VariableFoundEventArgs foundArgument) + private void ScanVariables(Variables variables, TreeNode parent, string currentPath) { if (this.CancellationPending) { return; } + TreeNode variablesFolder = AddFolder("Variables", parent); + int imageIndex = GetImageIndex(IconKeyVariableExpression); + foreach (Variable variable in variables) { try @@ -453,7 +613,7 @@ private void ScanVariables(Variables variables, VariableFoundEventArgs foundArgu // Check path to ensure variable is parented by current scope // only, not by child containers that inherit the variable - if (!variable.GetPackagePath().StartsWith(foundArgument.ObjectPath + ".Variables[")) + if (!variable.GetPackagePath().StartsWith(currentPath + ".Variables[")) { continue; } @@ -461,30 +621,35 @@ private void ScanVariables(Variables variables, VariableFoundEventArgs foundArgu string match; if (ExpressionMatch(variable.Expression, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectID = variable.ID; - info.ObjectPath = foundArgument.ObjectPath + ".Variables[" + variable.QualifiedName + "]"; - info.ObjectType = variable.GetType().Name; - info.PropertyName = variable.QualifiedName; - info.Value = variable.Expression; - info.IsExpression = variable.EvaluateAsExpression; - info.Icon = BIDSHelper.Resources.Versioned.Variable; + VariableFoundEventArgs info = new VariableFoundEventArgs(); + //info.ObjectID = variable.ID; + //info.ObjectPath = foundArgument.ObjectPath + ".Variables[" + variable.QualifiedName + "]"; + //info.ObjectType = variable.GetType().Name; + //info.PropertyName = variable.QualifiedName; + //info.Value = variable.Expression; + //info.IsExpression = variable.EvaluateAsExpression; + //info.Icon = BIDSHelper.Resources.Versioned.Variable; info.Match = match; OnRaiseVariableFound(info); + AddNode(variablesFolder, variable.QualifiedName, imageIndex, variable, true); + //AddNode(expressions, "Expression", GetImageIndex(IconKeyProperti1es), new PropertyExpression(propertyName, expression, PackageHelper.GetTypeFromTypeCode(property.Type)), true); } } catch { } } } - private void ScanProperties(IDTSPropertiesProvider provider, VariableFoundEventArgs foundArgument) + private void ScanProperties(IDTSPropertiesProvider provider, TreeNode parent) { if (this.CancellationPending) { return; } - bool isPipeline = (foundArgument.ObjectType == ObjectTypeDataFlowTask); + TreeNode properties = AddFolder("Properties", parent); + TreeNode expressions = AddFolder("PropertyExpressions", parent); + + //bool isPipeline = (foundArgument.ObjectType == ObjectTypeDataFlowTask); // New 2012 + interface implemented by Package, Sequence, DtsEventHandler, ForLoop, ForEachLoop // There are other objects that implement IDTSPropertiesProvider, and therefore support expressions, e.g. ConnectionManager, Variable @@ -499,7 +664,7 @@ private void ScanProperties(IDTSPropertiesProvider provider, VariableFoundEventA foreach (DtsProperty property in provider.Properties) { // Skip any expressuon properties on the Data Flow task, we deal with then in ScanPipeline explicitly - if (isPipeline && property.Name.StartsWith("[")) + if (property.Name.StartsWith("[")) { continue; } @@ -514,33 +679,18 @@ private void ScanProperties(IDTSPropertiesProvider provider, VariableFoundEventA string value = property.GetValue(provider) as string; if (!string.IsNullOrEmpty(value)) { - - string pathOverride; - if (property.Name.StartsWith("[")) + if (PropertyMatch(propertyName, value, out match)) { - pathOverride = foundArgument.ObjectPath + ".Properties" + property.Name + ""; + VariableFoundEventArgs foundArgument = new VariableFoundEventArgs(); + foundArgument.Match = match; + OnRaiseVariableFound(foundArgument); + AddNode(properties, propertyName, GetImageIndex(IconKeyProperty), new PropertyInfo(property, value), true); } - else - { - pathOverride = foundArgument.ObjectPath + ".Properties[" + property.Name + "]"; - } - - PropertyMatch(foundArgument, pathOverride, propertyName, value); } } #endregion #region Check property expression - // Check expression - - // TODO can we use IDTSPropertiesProviderEx.HasExpressions Property - // - - //if (!hasExpressions) - //{ - // continue; - //} - string expression = provider.GetExpression(property.Name); if (expression == null) { @@ -552,21 +702,10 @@ private void ScanProperties(IDTSPropertiesProvider provider, VariableFoundEventA if (ExpressionMatch(expression, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - if (property.Name.StartsWith("[")) - { - info.ObjectPath = foundArgument.ObjectPath + ".PropertyExpression" + property.Name + ""; - } - else - { - info.ObjectPath = foundArgument.ObjectPath + ".PropertyExpression[" + property.Name + "]"; - } - - info.PropertyName = property.Name; - info.Value = expression; - info.IsExpression = true; - info.Match = match; - OnRaiseVariableFound(info); + VariableFoundEventArgs foundArgument = new VariableFoundEventArgs(); + foundArgument.Match = match; + OnRaiseVariableFound(foundArgument); + AddNode(expressions, "Expression", GetImageIndex(IconKeyVariableExpression), new PropertyExpression(propertyName, expression, PackageHelper.GetTypeFromTypeCode(property.Type)), true); } #endregion @@ -589,42 +728,32 @@ private bool ExpressionMatch(string expression, out string match) return false; } - private void PropertyMatch(VariableFoundEventArgs foundArgument, string pathOverride, string propertyName, string value) + private bool PropertyMatch(string propertyName, string value, out string match) { - string match; if (propertyName == "ReadOnlyVariables" || propertyName == "ReadWriteVariables") { // Comma delimited list of variable names, split and then search foreach (string item in value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { - if (PropertyMatch(item, out match)) + if (PropertyMatchEval(item, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = pathOverride; - info.PropertyName = propertyName; - info.Value = value; - info.IsExpression = false; - info.Match = match; - OnRaiseVariableFound(info); + return true; } } } else { - if (PropertyMatch(value, out match)) + if (PropertyMatchEval(value, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = pathOverride; - info.PropertyName = propertyName; - info.Value = value; - info.IsExpression = false; - info.Match = match; - OnRaiseVariableFound(info); + return true; } } + + match = null; + return false; } - private bool PropertyMatch(string value, out string match) + private bool PropertyMatchEval(string value, out string match) { foreach (string test in this.properytMatches) { @@ -653,56 +782,56 @@ protected virtual void OnRaiseVariableFound(VariableFoundEventArgs e) } } - private void CheckExecuteSQLTask(TaskHost taskHost, VariableFoundEventArgs foundArgument) + private void CheckExecuteSQLTask(TaskHost taskHost, TreeNode parent) { ExecuteSQLTask task = taskHost.InnerObject as ExecuteSQLTask; + TreeNode parameterBindings = AddFolder("ParameterBindings", parent); + foreach (IDTSParameterBinding binding in task.ParameterBindings) { string match; string value = binding.DtsVariableName; - if (!string.IsNullOrEmpty(value) && PropertyMatch(value, out match)) + if (!string.IsNullOrEmpty(value) && PropertyMatchEval(value, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = foundArgument.ObjectPath + ".ParameterBindings[" + binding.ParameterName + "]"; - info.PropertyName = binding.ParameterName.ToString(); - info.Value = value; - info.IsExpression = false; + VariableFoundEventArgs info = new VariableFoundEventArgs(); info.Match = match; OnRaiseVariableFound(info); + AddNode(parameterBindings, binding.ParameterName.ToString(), GetImageIndex(IconKeyProperty), binding, true); } } + TreeNode resultSetBindings = AddFolder("ResultSetBindings", parent); + foreach (IDTSResultBinding binding in task.ResultSetBindings) { string match; string value = binding.DtsVariableName; - if (!string.IsNullOrEmpty(value) && PropertyMatch(value, out match)) + if (!string.IsNullOrEmpty(value) && PropertyMatchEval(value, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = foundArgument.ObjectPath + ".ResultSetBindings[" + binding.ResultName + "]"; - info.PropertyName = binding.ResultName.ToString(); - info.Value = value; - info.IsExpression = false; + VariableFoundEventArgs info = new VariableFoundEventArgs(); info.Match = match; OnRaiseVariableFound(info); + AddNode(resultSetBindings, binding.ResultName.ToString(), GetImageIndex(IconKeyProperty), binding, true); } } } - private void CheckExpressionTask(TaskHost taskHost, VariableFoundEventArgs foundArgument) + private void CheckExpressionTask(TaskHost taskHost, TreeNode parent) { // Expression task has the Expression property which we need to treat as an expression rather than a literal value as we do for normal properties. // Get the Expression value and run an expression matct test DtsProperty property = taskHost.Properties["Expression"]; string expression = property.GetValue(taskHost).ToString(); - PropertyAsExpressionMatch(property.Name, expression, foundArgument); + PropertyAsExpressionMatch(property, expression, parent); } - private void CheckExecutePackageTask(TaskHost taskHost, VariableFoundEventArgs foundArgument) + private void CheckExecutePackageTask(TaskHost taskHost, TreeNode parent) { ExecutePackageTask task = taskHost.InnerObject as ExecutePackageTask; + TreeNode parameterAssignments = AddFolder("ParameterAssignments", parent); + // ParameterAssignments doesn't support foreach enumeration, so use for loop instead. for (int i = 0; i < task.ParameterAssignments.Count; i++) { @@ -710,83 +839,78 @@ private void CheckExecutePackageTask(TaskHost taskHost, VariableFoundEventArgs f string match; string value = assignment.BindedVariableOrParameterName; - if (!string.IsNullOrEmpty(value) && PropertyMatch(value, out match)) + if (!string.IsNullOrEmpty(value) && PropertyMatchEval(value, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = foundArgument.ObjectPath + ".ParameterAssignments[" + assignment.ParameterName + "]"; - info.PropertyName = assignment.ParameterName.ToString(); - info.Value = value; - info.IsExpression = false; + VariableFoundEventArgs info = new VariableFoundEventArgs(); info.Match = match; OnRaiseVariableFound(info); + AddNode(parameterAssignments, assignment.ParameterName.ToString(), GetImageIndex(IconKeyProperty), assignment, true); } } } - private void CheckForEachLoop(ForEachLoop forEachLoop, VariableFoundEventArgs foundArgument) + private void CheckForEachLoop(ForEachLoop forEachLoop, TreeNode parent) { // Check properties of loop itself - foundArgument.Type = typeof(ForEachLoop); - ScanProperties(forEachLoop, foundArgument); + //foundArgument.Type = typeof(ForEachLoop); + ScanProperties(forEachLoop, parent); // Check properties of enumerator, when present ForEachEnumeratorHost enumerator = forEachLoop.ForEachEnumerator; if (enumerator == null) return; - VariableFoundEventArgs foundEnumerator = new VariableFoundEventArgs(foundArgument); - foundEnumerator.ObjectPath = foundArgument.ObjectPath + "\\" + foundEnumerator.Type.Name + "."; - foundEnumerator.Type = enumerator.GetType(); - ScanProperties(enumerator, foundEnumerator); + TreeNode enumeratorFolder = AddFolder(enumerator.GetType().Name, parent); + ScanProperties(enumerator, enumeratorFolder); + TreeNode variableMappings = AddFolder("VariableMappings", parent); foreach (ForEachVariableMapping mapping in forEachLoop.VariableMappings) { string match; string value = mapping.VariableName; - if (!string.IsNullOrEmpty(value) && PropertyMatch(value, out match)) + if (!string.IsNullOrEmpty(value) && PropertyMatchEval(value, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = foundArgument.ObjectPath + ".VariableMappings[" + mapping.ValueIndex.ToString() + "]"; - info.PropertyName = mapping.ValueIndex.ToString(); - info.Value = value; - info.IsExpression = false; + VariableFoundEventArgs info = new VariableFoundEventArgs(); info.Match = match; OnRaiseVariableFound(info); + AddNode(variableMappings, mapping.ValueIndex.ToString(), GetImageIndex(IconKeyProperty), mapping, true); } } } - private void CheckForLoop(IDTSPropertiesProvider forLoop, VariableFoundEventArgs foundArgument) + private void CheckForLoop(IDTSPropertiesProvider forLoop, TreeNode parent) { // Check regular properties of the loop for variables and regular property expressions - foundArgument.Type = typeof(ForEachLoop); - ScanProperties(forLoop, foundArgument); + ScanProperties(forLoop, parent); // Check explicit expression properties as expressions, missed if we are looking for literal variables. DtsProperty property; property = forLoop.Properties["AssignExpression"]; - PropertyAsExpressionMatch(property.Name, property.GetValue(forLoop).ToString(), foundArgument); + PropertyAsExpressionMatch(property, property.GetValue(forLoop).ToString(), parent); property = forLoop.Properties["EvalExpression"]; - PropertyAsExpressionMatch(property.Name, property.GetValue(forLoop).ToString(), foundArgument); + PropertyAsExpressionMatch(property, property.GetValue(forLoop).ToString(), parent); property = forLoop.Properties["InitExpression"]; - PropertyAsExpressionMatch(property.Name, property.GetValue(forLoop).ToString(), foundArgument); + PropertyAsExpressionMatch(property, property.GetValue(forLoop).ToString(), parent); } - private void PropertyAsExpressionMatch(string propertyName, string expression, VariableFoundEventArgs foundArgument) + private void PropertyAsExpressionMatch(DtsProperty property, string expression, TreeNode parent) { string match; if (ExpressionMatch(expression, out match)) { - VariableFoundEventArgs info = new VariableFoundEventArgs(foundArgument); - info.ObjectPath = foundArgument.ObjectPath + ".Properties[" + propertyName + "]"; - info.PropertyName = propertyName; - info.Value = expression; - info.IsExpression = true; + VariableFoundEventArgs info = new VariableFoundEventArgs(); info.Match = match; OnRaiseVariableFound(info); + + if (parent != null) + { + TreeNode propertiesNode = parent.Nodes["Properties"]; + System.Diagnostics.Debug.Assert(!(parent != null && propertiesNode == null), "Properties node doesn't exist when it should already. We will lose this property match. Find the Properties node."); + AddNode(propertiesNode, property.Name, GetImageIndex(IconKeyVariableExpression), new PropertyInfo(property, expression), true); + } } } } @@ -800,29 +924,69 @@ public VariableFoundEventArgs() public VariableFoundEventArgs(VariableFoundEventArgs variableFoundEventArgs) { - this.Icon = variableFoundEventArgs.Icon; - this.Type = variableFoundEventArgs.Type; - this.IsExpression = variableFoundEventArgs.IsExpression; - this.ContainerID = variableFoundEventArgs.ContainerID; + //this.Icon = variableFoundEventArgs.Icon; + //this.Type = variableFoundEventArgs.Type; + //this.IsExpression = variableFoundEventArgs.IsExpression; + //this.ContainerID = variableFoundEventArgs.ContainerID; this.Match = variableFoundEventArgs.Match; - this.ObjectID = variableFoundEventArgs.ObjectID; - this.ObjectName = variableFoundEventArgs.ObjectName; - this.ObjectPath = variableFoundEventArgs.ObjectPath; - this.ObjectType = variableFoundEventArgs.ObjectType; - this.PropertyName = variableFoundEventArgs.PropertyName; - this.Value = variableFoundEventArgs.Value; - } - - public Icon Icon { get; set; } - public Type Type { get; set; } - public bool IsExpression { get; set; } - public string ContainerID { get; set; } + //this.ObjectID = variableFoundEventArgs.ObjectID; + //this.ObjectName = variableFoundEventArgs.ObjectName; + //this.ObjectPath = variableFoundEventArgs.ObjectPath; + //this.ObjectType = variableFoundEventArgs.ObjectType; + //this.PropertyName = variableFoundEventArgs.PropertyName; + //this.Value = variableFoundEventArgs.Value; + } + public string Match { get; set; } - public string ObjectID { get; set; } - public string ObjectName { get; set; } - public string ObjectPath { get; set; } - public string ObjectType { get; set; } - public string PropertyName { get; set; } - public string Value { get; set; } + } + + public class PropertyExpression + { + public PropertyExpression(string name, string expression, Type type) + { + this.PropertyName = name; + this.Expression = expression; + this.Type = type; + } + + [ParenthesizePropertyName(), Browsable(true), Category("General")] + public string PropertyName { get; private set; } + + [Category("General")] + public object Expression { get; private set; } + + [Category("General")] + public Type Type { get; private set; } + } + + [DisplayName("Property")] + public class PropertyInfo + { + // We don't need an IDTSCustomProperty100 version because that already does a good job of displaying both the property information AND the value. + // DtsProper doesn't include the value, hence we use thso wrapper for teh TreeView tag object + + public PropertyInfo(DtsProperty property, object value) + { + this.Name = property.Name; + this.Value = value; + this.Type = PackageHelper.GetTypeFromTypeCode(property.Type); + this.Get = property.Get; + this.Set = property.Set; + } + + [ParenthesizePropertyName(), Browsable(true), Category("General")] + public string Name { get; private set; } + + [Category("General")] + public bool Get { get; private set; } + + [Category("General")] + public bool Set { get; private set; } + + [Category("General")] + public object Value { get; private set; } + + [Category("General")] + public Type Type { get; private set; } } } diff --git a/SSIS/PackageHelper.cs b/SSIS/PackageHelper.cs index 8fb6118..0de0ba4 100644 --- a/SSIS/PackageHelper.cs +++ b/SSIS/PackageHelper.cs @@ -4,20 +4,9 @@ using System.Collections.Generic; using Microsoft.SqlServer.Dts.Pipeline.Wrapper; using Microsoft.SqlServer.Dts.Runtime; - + using System.Runtime.InteropServices; internal class PackageHelper { - /// - /// All managed components in the data flow share the same wrapper, identified by this GUID. - /// The specific type of managed component is identified by the UserComponentTypeName - /// custom property of the component. - /// - public const string ManagedComponentWrapper = "{33D831DE-5DCF-48F0-B431-4D327B9E785D}";//{bf01d463-7089-41ee-8f05-0a6dc17ce633}"; - - - // Script Component ID - public const string ScriptComponentID = "Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost, Microsoft.SqlServer.TxScript, Version=12.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"; - public const string PackageCreationName = "Package"; public const string EventHandlerCreationName = "EventHandler"; public const string ConnectionCreationName = "Connection"; @@ -26,18 +15,55 @@ internal class PackageHelper public const string ForEachLoopCreationName = "ForEachLoop"; /// - /// Private field for ComponentInfos property + /// Private field for the ComponentInfos property /// private static ComponentInfos componentInfos = new ComponentInfos(); /// - /// Private field for ComponentInfos property + /// Private field for the ComponentInfos property /// private static ComponentInfos controlInfos = new ComponentInfos(); + /// + /// Indicates if the ComponentInfos propertry has been initialied or not, prevents repeating the expensive component search + /// private static bool componentInitialised = false; + + /// + /// Object used for locking + /// private static object componentLock = new object(); + /// + /// Private field for the ManagedComponentWrapper property + /// + private static string managedComponentWrapper; + + /// + /// All managed components in the data flow share the same wrapper, identified by this GUID. + /// The specific type of managed component is identified by the UserComponentTypeName custom property of the component. + /// + public static string ManagedComponentWrapper + { + get + { + if (managedComponentWrapper == null) + { + // This value changed unexpectedly + // SQL2014 - {33D831DE-5DCF-48F0-B431-4D327B9E785D} + // SQL2005 - {BF01D463-7089-41EE-8F05-0A6DC17CE633} + // See documentation https://technet.microsoft.com/nl-nl/library/microsoft.sqlserver.dts.pipeline.wrapper.cmanagedcomponentwrapperclass(v=sql.90).aspx + + // To prevent future issues, we will get it from the object itself. + GuidAttribute attribute = (GuidAttribute)Attribute.GetCustomAttribute(typeof(CManagedComponentWrapperClass), typeof(GuidAttribute)); + managedComponentWrapper = attribute.Value; + } + + return managedComponentWrapper; + } + } + + public static List GetControlFlowObjects(DtsContainer container) { List returnItems = new List(); @@ -97,6 +123,8 @@ public static ComponentInfos ComponentInfos } else { + // Script Component ID + ////public const string ScriptComponentID = "Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost, Microsoft.SqlServer.TxScript, Version=12.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"; ////if (pipelineComponentInfo.ID == ScriptComponentID) ////{ //// // For the script component on SQL 2014, PipelineComponentInfo shows an ID of Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost, Microsoft.SqlServer.TxScript, Version=12.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91 @@ -145,11 +173,15 @@ public static ComponentInfos ControlFlowInfos } // Special containers, see GetCreationName usage + // TODO: Consider switching to Microsoft.DataTransformationServices.Design.SharedIcons instead of supplying our own, but is that available in all SQL versions, or just 2014? controlInfos.Add(PackageCreationName, new ComponentInfo(BIDSHelper.Resources.Common.Package)); controlInfos.Add(EventHandlerCreationName, new ComponentInfo(BIDSHelper.Resources.Versioned.Event)); controlInfos.Add(SequenceCreationName, new ComponentInfo(BIDSHelper.Resources.Versioned.Sequence)); + controlInfos.Add("STOCK:" + SequenceCreationName.ToUpper(), new ComponentInfo(BIDSHelper.Resources.Versioned.Sequence)); controlInfos.Add(ForLoopCreationName, new ComponentInfo(BIDSHelper.Resources.Versioned.ForLoop)); + controlInfos.Add("STOCK:" + ForLoopCreationName.ToUpper(), new ComponentInfo(BIDSHelper.Resources.Versioned.ForLoop)); controlInfos.Add(ForEachLoopCreationName, new ComponentInfo(BIDSHelper.Resources.Versioned.ForEachLoop)); + controlInfos.Add("STOCK:" + ForEachLoopCreationName.ToUpper(), new ComponentInfo(BIDSHelper.Resources.Versioned.ForEachLoop)); // Connections - Cannot get them as with components - Attribute pattern is broken, only used by third // parties. The Connection toolbox doesn't use it so MS haven't attributed their connections. diff --git a/SSIS/ParametersWindowPlugin.cs b/SSIS/ParametersWindowPlugin.cs index d2be799..508a7ef 100644 --- a/SSIS/ParametersWindowPlugin.cs +++ b/SSIS/ParametersWindowPlugin.cs @@ -266,7 +266,7 @@ private void FindReferencesButtonClick() Parameter parameter = GetParameterForRow(selectedRow); if (parameter == null) return; - FindReferences dialog = new FindReferences(); + FindVariableReferences dialog = new FindVariableReferences(); dialog.Show(package, parameter); } catch (Exception ex) diff --git a/SSIS/VariablesMove.Designer.cs b/SSIS/VariablesMove.Designer.cs index 5dfa2f4..fc976d0 100644 --- a/SSIS/VariablesMove.Designer.cs +++ b/SSIS/VariablesMove.Designer.cs @@ -39,20 +39,19 @@ private void InitializeComponent() // // treeView // - this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); + this.treeView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.treeView.HideSelection = false; this.treeView.ImageIndex = 0; this.treeView.ImageList = this.imageList; + this.treeView.ItemHeight = 18; this.treeView.Location = new System.Drawing.Point(13, 36); this.treeView.Name = "treeView"; this.treeView.SelectedImageIndex = 0; - this.treeView.Size = new System.Drawing.Size(260, 237); + this.treeView.Size = new System.Drawing.Size(300, 237); this.treeView.TabIndex = 0; this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect); - this.treeView.ItemHeight = 18; - // // imageList // @@ -64,7 +63,7 @@ private void InitializeComponent() // this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnCancel.Location = new System.Drawing.Point(198, 285); + this.btnCancel.Location = new System.Drawing.Point(238, 285); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(75, 23); this.btnCancel.TabIndex = 2; @@ -75,7 +74,7 @@ private void InitializeComponent() // this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; - this.btnOK.Location = new System.Drawing.Point(117, 285); + this.btnOK.Location = new System.Drawing.Point(157, 285); this.btnOK.Name = "btnOK"; this.btnOK.Size = new System.Drawing.Size(75, 23); this.btnOK.TabIndex = 3; @@ -110,7 +109,7 @@ private void InitializeComponent() this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.btnCancel; - this.ClientSize = new System.Drawing.Size(285, 320); + this.ClientSize = new System.Drawing.Size(325, 320); this.Controls.Add(this.radCopy); this.Controls.Add(this.radMove); this.Controls.Add(this.btnOK); diff --git a/SSIS/VariablesMove.resx b/SSIS/VariablesMove.resx index e93f85e..843ba53 100644 --- a/SSIS/VariablesMove.resx +++ b/SSIS/VariablesMove.resx @@ -112,12 +112,15 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 17, 17 + + 42 + \ No newline at end of file diff --git a/SSIS/VariablesWindowPluginPartial.cs b/SSIS/VariablesWindowPluginPartial.cs index 4917c1f..94b3e0f 100644 --- a/SSIS/VariablesWindowPluginPartial.cs +++ b/SSIS/VariablesWindowPluginPartial.cs @@ -34,8 +34,8 @@ private void FindReferencesButtonClick() if (variable == null) return; - FindReferences dialog = new FindReferences(); - dialog.EditExpressionSelected += new EventHandler(findReferences_EditExpressionSelected); + FindVariableReferences dialog = new FindVariableReferences(); + //dialog.EditExpressionSelected += new EventHandler(findReferences_EditExpressionSelected); dialog.Show(package, variable); }