Visual Studio Automation: Programatically get a project output directory - visual-studio

I want to programatically get a project output directory in a Visual Studio macro.
I managed to get a string of the path (through prj.ConfigurationManager.ActiveConfiguration.Properties and looking at property OutputDirectory) but this string may contain macros such as $(foo) where foo is defined in a property sheet or whatnot.
How do I resolve this output directory string to the 'real' directory?

I wrote this function for my macros which searches for full absolute output path by substring.
Function FindOutBinaryNameByExtension(ByVal prj As EnvDTE.Project, ByVal extName As String) As String
FindOutBinaryNameByExtension = Nothing
Dim cm As ConfigurationManager = prj.ConfigurationManager
If cm IsNot Nothing Then
Dim ac As Configuration = cm.ActiveConfiguration
For Each grpOut In ac.OutputGroups
If grpOut.DisplayName = "Primary output" Then
Dim lst As Array = grpOut.FileURLs
For i As Long = 0 To lst.Length - 1
Dim fileName As String = lst.GetValue(i)
If fileName.Contains(extName) Then
FindOutBinaryNameByExtension = fileName
Exit Function
End If
Next
End If
Next
End If
End Function

Related

User-defined type not defined vb6

I am attempting to 'save' the context of a text box in vb6 to a *.ini file, so that it can be used in a later part of the program. (i.e. the user would enter something into the text box, then later in the program, a label would appear with the user-entered, saved information).
I used the following code which I copied from the source of someone else's program, however it hasn't worked:
Dim fsys As New FileSystemObject
Dim outstream As TextStream
Dim write1 As String
Dim val1 As String
val1 = Text1.Text
inisettings = App.Path & "\Variables.ini"
Set outstream = fsys.OpenTextFile(inisettings, ForWriting, False, TristateFalse)
outstream.WriteLine (val1)
Set outstream = Nothing
This is the result:
Does anyone have any way to save data for later?
FileSystemObject lives inside an external library, to use it click Project then References and tick Microsoft Scripting Runtime.
You don't actually need to do any of that, the code below uses VB's built-in functionality to write a file.
Dim hF As Integer
hF = FreeFile()
Open App.Path & "\Variables.ini" For Output As #hF
Print #hF, val1
Close #hF
You must declare TristateFalse and give it a value like 0, 1 or 2.
You can take a look at this link: https://msdn.microsoft.com/en-us/subscriptions/bxw6edd3(v=vs.84).aspx
The reason why you are getting this error is because you don't have a reference to the Microsoft Scripting Runtime library. Follow the below instructions while in your VB6 project:
From the top menu, click on Project > References.
From the list, check the item entitled "Microsoft Scripting Runtime".
Click OK.
This will resolve your immediate error however your code still has some other issues. First off, you forgot to declare the variable inisettings. I am going to assume that you will want to always overwrite the entire file each time you update the INI file so you want to use the method CreateTextFile instead of OpenTextFile.
Dim fsys As New FileSystemObject
Dim outstream As TextStream
Dim write1 As String
Dim val1 As String
Dim inisettings As String
val1 = Text1.Text
inisettings = App.Path & "\Variables.ini"
Set outstream = fsys.CreateTextFile(inisettings, True, False)
Call outstream.WriteLine(val1)
Set outstream = Nothing
Good luck!

Visual Basic - Copying the only Recently Modified Files to another Folder

the following code I have hear will extract all the files inside of a certain folder, and then copy all of them and place them into another folder. My question today is how do I modify this code so that only files extracted from the original folder have been recently modified. Even if you could show me how to extract only the files that have been modified from today would be great. Thanks to all of you who help!
Imports System.IO
Public Class frmExtractionator
Dim txtFiles1 As Control
Private Sub btnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Dim sourceDirectory As String = "E:\CopierFolderforTestDriveCapstone"
Dim archiveDirectory As String = "E:\FilesExtracted"
Try
'DeleteUnmodifiedFiles(sourceDirectory, -14)
Dim txtFiles = Directory.EnumerateFiles(sourceDirectory)
If (Not System.IO.Directory.Exists(archiveDirectory)) Then
System.IO.Directory.CreateDirectory(archiveDirectory)
End If
For Each currentFileLoc As String In txtFiles
Dim fileName = currentFileLoc.Substring(sourceDirectory.Length + 1)
File.Move(currentFileLoc, Path.Combine(archiveDirectory, fileName))
Next
Catch eT As Exception
Console.WriteLine(eT.Message)
End Try
End Sub
You can use the Directory.GetLastWriteTime Method to determine when the file was last written to.
From Link:
Returns the date and time the specified file or directory was last written to.
Dim checkDate As Date = Date.Parse("01/1/2013")
For Each currentFileLoc As String In txtFiles
Dim fileName = currentFileLoc.Substring(sourceDirectory.Length + 1)
If Directory.GetLastWriteTime(Path.Combine(sourceDirectory, fileName)) > checkDate Then
File.Move(currentFileLoc, Path.Combine(archiveDirectory, fileName))
End If
Next

Outputting list of files that are part of a Visual Studio Project

I'm looking for a way to output a list of all files in a Visual Studio project for documentation purposes.
I would have thought this would be possible but I can't find any info. I'm not talking about using Sandcastle to hook up to XML comments, I just want a "simple" indented list of project files.
I'm guessing we could run an xsl file against the Proj file but hopefully somebody has already got a solution for this? Ideally this would work on both 2008 and 2010.
The VS2008 sample macros already contain a macro that practically does this (prints a list of source/header file to the output window). It's called ListProj1, under the Utilities samples. Here's the code in case you don't have it:
Sub ListProj()
Dim project As Project
Dim projectObjects As Object()
Dim window As Window
Dim target As Object
window = DTE.Windows.Item(Constants.vsWindowKindCommandWindow)
projectObjects = DTE.ActiveSolutionProjects
If projectObjects.Length = 0 Then
Exit Sub
End If
project = DTE.ActiveSolutionProjects(0)
If (DTE.ActiveWindow Is window) Then
target = window.Object
Else
target = GetOutputWindowPane("List Project")
target.Clear()
End If
ListProjAux(project.ProjectItems(), 0, target)
End Sub
Sub ListProjAux(ByVal projectItems As EnvDTE.ProjectItems, ByVal level As Integer, ByVal outputWinPane As Object)
Dim projectItem As EnvDTE.ProjectItem
For Each projectItem In projectItems
If projectItem.Collection Is projectItems Then
Dim projectItems2 As EnvDTE.ProjectItems
Dim notSubCollection As Boolean
OutputItem(projectItem, level, outputWinPane)
'' Recurse if this item has subitems ...
projectItems2 = projectItem.ProjectItems
notSubCollection = projectItems2 Is Nothing
If Not notSubCollection Then
ListProjAux(projectItems2, level + 1, outputWinPane)
End If
End If
Next
End Sub
Sub OutputItem(ByVal projectItem As EnvDTE.ProjectItem, ByVal level As Integer, ByVal outputWinPane As Object)
Dim i As Integer = 0
While (i < level)
outputWinPane.OutputString(" ")
i = i + 1
End While
outputWinPane.OutputString(projectItem.FileNames(1))
outputWinPane.OutputString(Microsoft.VisualBasic.Constants.vbCrLf)
End Sub

Report error/warning if missing files in project/solution in Visual Studio

Is there a way for Visual Studio to report an error/warning when you build a solution that has missing files (yellow triangle icon with exclamation) that do have necessarily cause a compile error? Like a missing config file that is read during run-time.
Thanks
You need to define an EnvironmentEvents macro. See the general description on how to do this here: Customize Your Project Build Process.
And here is the code you can directly paste in the macro environment to check missing files:
Private Sub BuildEvents_OnBuildBegin(ByVal Scope As EnvDTE.vsBuildScope, ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildBegin
For Each proj As Project In DTE.Solution.Projects
For Each item As ProjectItem In proj.ProjectItems
If (item.Kind <> "{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}") Then ' only check physical file items
Continue For
End If
For i As Integer = 1 To item.FileCount
Dim path As String = item.FileNames(i)
If Not System.IO.File.Exists(item.FileNames(i)) Then
WriteToBuildWindow("!! Missing file:" & item.FileNames(i) + " in project " + proj.Name)
End If
Next
Next
Next
End Sub
Public Sub WriteToBuildWindow(ByVal text As String)
Dim ow As OutputWindow = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object
Dim build As OutputWindowPane = ow.OutputWindowPanes.Item("Build")
build.OutputString(text & Environment.NewLine)
End Sub
It will display the "missing file" text directly in the Visual Studio "Build" output window. It should be fairly easy to understand and tune to your needs. For example, you could add errors to the error output.
When we had missing files, we were getting crazy compile errors, like unable to write xyz.pdb even though the file ended up getting written. I took what Simon had provided (thanks!) and flipped it around a bit; specifically, I added a bit of recursion and added support for folders and files with sub-files (e.g. datasets, code-behinds).
Private Sub BuildEvents_OnBuildBegin(ByVal Scope As EnvDTE.vsBuildScope, ByVal Action As EnvDTE.vsBuildAction) Handles BuildEvents.OnBuildBegin
For Each proj As Project In DTE.Solution.Projects
walkTree(proj.ProjectItems, False)
Next
End Sub
Private Sub walkTree(ByVal list As ProjectItems, ByVal showAll As Boolean)
For Each item As ProjectItem In list
' from http://msdn.microsoft.com/en-us/library/z4bcch80(v=vs.80).aspx
' physical files: {6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}
' physical folders: {6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}
If (item.Kind = "{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}" OrElse _
item.Kind = "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}") Then
For i As Integer = 1 To item.FileCount ' appears to be 1 all the time...
Dim existsOrIsFolder As Boolean = (item.Kind = "{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}" OrElse System.IO.File.Exists(item.FileNames(i)))
If (showAll OrElse _
existsOrIsFolder = False) Then
WriteToBuildWindow(String.Format("{0}, {1}, {2} ", existsOrIsFolder, item.ContainingProject.Name, item.FileNames(i)))
End If
Next
If (item.ProjectItems.Count > 0) Then
walkTree(item.ProjectItems, showAll)
End If
End If
Next
End Sub
Private Sub WriteToBuildWindow(ByVal text As String)
Dim ow As OutputWindow = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput).Object
Dim build As OutputWindowPane = ow.OutputWindowPanes.Item("Build")
build.OutputString(text & Environment.NewLine)
End Sub
In case anyone else stumbles across this thread, there's a plugin that will give you a build error when you have files missing from your project.
If you happen to have a linux-like environment with access to the project folder (for instance, if you use git for version control, you can probably use the included Git Bash for this, or if you use Cygwin), here's my really quick and dirty way:
grep '<Content Include="' "project_file.csproj" | sed 's/^.*"\([^"]*\)".*/\1/' | sed 's/\\/\//g' | xargs -d'\n' ls > /dev/null
(How this works: I try to ls every file named in the project, and send the stdout output of the ls command to /dev/null, so it will be hidden. If any files do not exist, ls will barf their names to stderr rather than stdout, so those will be visible.)
Note that this doesn't understand URL-encoded escapes, so you will get a few false positives if your project contains filenames with characters like '(' in them.

Visual Studio Macro to Paste similar to String.Format

I'd like to be able to cut/copy a string like "<strong>{0}</strong>" for example.
I'd like to then select some code such as "Hello, World" and then invoke a macro which will result in "<strong>Hello, World</strong>".
How could you do this?
Update: WHY do I want to do this?
I could just make a macro or shortcut to add something specific like a <strong> tag to a selection. However, my idea to create any sort of "surround with" paste behavior on the fly.
Fairly often, I paste in a list of fields or properties. So from somewhere else I get
PersonID
FirstName
LastName
And just as an example, I know I want to set those up as
FieldName = dataRow("FieldName").Value
With my magic macro, I could select the following and press CTRL+C to get it in my clipboard:
{0} = dataRow("{0}").Value
Then all I have to do is go line by line and apply my magic paste.
Fun little project.
Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Public Module StringFormatModule
Private clipText As String
Public Property ClipboardText() As String
Get
RunThread(AddressOf GetClipboardText)
Return clipText
End Get
Set(ByVal value As String)
clipText = value
RunThread(AddressOf CopyToClipboard)
End Set
End Property
Private Function RunThread(ByVal fct As Threading.ThreadStart)
Dim thread As New Threading.Thread(fct)
thread.ApartmentState = Threading.ApartmentState.STA
thread.Start()
thread.Join()
End Function
Private Sub GetClipboardText()
clipText = My.Computer.Clipboard.GetText()
End Sub
Private Sub CopyToClipboard()
My.Computer.Clipboard.SetText(clipText)
End Sub
Sub FormatSelectedTextWithCopiedText()
Dim formatString As String
formatString = ClipboardText
Dim token As String
Dim selectedText As TextSelection
selectedText = DTE.ActiveDocument.Selection
token = selectedText.Text
selectedText.Text = String.Format(formatString, token)
End Sub
End Module
I borrowed the clipboard code from here.
This does work. I tested it on a text file, copy your formatstring into the clipboard (ctrl-c), highlight the text you want to format, and then run the macro (i just doubleclicked it from the macro explorer but you could make a keyboard shortcut).
Wouldn't it be better to define a macro that added the 'strong' tags around the selected text? Then you could assign it to Ctrl+B or whatever.
Having to select both chunks of text and invoking the macro twice seems too much like hard work to me.
(maybe you need to explain why you want to do this)
Instead of {0}, I use &. Assign macro to Ctrl+Q and you are all set!
' Wraps the current selection with the specified text. Use the & character as the anchor for the selected text.
Public Sub WrapSelection()
Dim selection As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
DTE.UndoContext.Open("Wrap Selection")
Try
Dim sInput As String = InputBox("Wrap(&&, state)")
If Len(sInput) > 0 Then
Dim sContent As String = selection.Text
Dim iStart As Integer = InStr(sInput, "&") - 1
Dim iEnd As Integer = InStrRev(sInput, "&")
selection.Insert(sInput.Substring(0, iStart) + sContent + sInput.Substring(iEnd), vsInsertFlags.vsInsertFlagsContainNewText)
'selection.Insert(sInput.Substring(iEnd), vsInsertFlags.vsInsertFlagsInsertAtEnd)
End If
Catch ex As Exception
DTE.UndoContext.SetAborted()
MsgBox(ex.Message)
Finally
'If an error occured, then need to make sure that the undo context is cleaned up.
'Otherwise, the editor can be left in a perpetual undo context
DTE.UndoContext.Close()
End Try
End Sub

Resources