I have been trying different Powershell translations of a "find and edit strikethrough" function originally written in VB and can't find one that works. More broadly, I am converting a lot of old VB scripts to Powershell and there is certain syntax and commands that don't carry over neatly. Beyond this particular script, anybody have any resources that might help me do this more effectively than just guess and check?
Here is the VB function to replace strikethrough text:
Dim d As Object = GetDocument(handle,document_name)
Dim r As Object = d.Range
With r.Find
.ClearFormatting
.Font.StrikeThrough = True
.Text = vfindtext
.Replacement.Text = vreplacetext
.Execute (Replace:=vReplaceType)
End With
And here is the last thing I tried in Powershell:
$word = new-object -comobject Word.application
$word.visible = $true
$doc1 = $word.documents.open($filename)
$range1 = $doc1.Range
$Format = $true
$range1.Find.ClearFormatting
$range1.Find.Font.Strikethrough
$range1.Find.Text
$range1.Find.Replacement.Text
$range1.Find.Text.Excecute.Replace(2)
I've tried several variations of this, different orders of method calls, different variable setups etc but can't find one that works.
I am trying to find strikethroughtext and either remove the strikethrough or delete the text. Right now I can't even get Powershell to identify the strikethrough though. Any ideas? Thanks!
This code is as noted not complete.
Try this refactor for the last code segment of your post, as a close-to-direct translation of the VBA:
$range1.Find.ClearFormatting
$range1.Find.Font.StrikeThrough = $true
$range1.Find.Text = $vfindtext
$range1.Find.Replacement.Text = $vreplacetext
$range1.Find.Execute($true, $false, $vReplaceType)
Ok figured it out. Using .range wasn't grabbing anything so all further methods ($range1.Find.etc...) were being called on a null variable. When I swapped $doc1.range for $doc1.content the assigned variable ($range1) became a com object and I was able to remove the strikethrough. This also fixed a remove highlight function I was trying. Below is the code that worked:
$word = new-object -comobject Word.application
$word.visible = $true
$doc1 = $word.documents.open($filename)
$range1 = $doc1.Content
$range1.Find.Font.StrikeThrough
$range1.Font.StrikeThrough = 0
$test = $range1.Find.Highlight
$range1.HighlightColorIndex = 0
Related
Using DocumentFormat.OpenXML, I am trying to add a custom property to a Word document and then later read the property. The following code "appears" to do just that:
Dim os As OpenSettings = New OpenSettings() With {
.AutoSave = False
}
Dim propVal As String = "Test Value"
Using doc As WordprocessingDocument = WordprocessingDocument.Open(filename, True, os)
Dim cPart As CustomFilePropertiesPart = doc.CustomFilePropertiesPart
If cPart Is Nothing Then
cPart = doc.AddCustomFilePropertiesPart
cPart.Properties = New DocumentFormat.OpenXml.CustomProperties.Properties()
End If
Dim cPart As CustomFilePropertiesPart = doc.CustomFilePropertiesPart
Dim cProps As Properties = cPart.Properties
For Each prop As CustomDocumentProperty In cProps
If prop.Name = "TranscriptID" Then
prop.Remove()
Exit For
End If
Next
Dim newProp As CustomDocumentProperty = New CustomDocumentProperty() With {
.Name = "TranscriptID"
}
newProp.VTBString = New VTBString(propVal)
newProp.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
cProps.AppendChild(newProp)
Dim pid As Integer = 2
For Each item As CustomDocumentProperty In cProps
item.PropertyId = pid
pid += 1
Next
cProps.Save()
End Using
This code is modeled after code found here:
https://learn.microsoft.com/en-us/office/open-xml/how-to-set-a-custom-property-in-a-word-processing-document
It appears to work in this scenario:
Execute code from above.
Execute code from above again.
At #2 I expect to find the CustomFilePropertiesPart and the property value and my expectation is met.
The problem appears in this scenario:
Execute code from above.
Open document using Microsoft Word, save and close.
Execute code from above again.
What happens in this scenario is that the CustomFilePropertiesPart is missing, whereas it should be found. It is as if Microsoft Word does not successfully read this object, so when the document is save, the object is lost. This suggests to me that there is something that there is something wrong with my code. If you can see what it is, or if you have a comparable working example that I could compare it with, I would appreciate hearing from you. I feel like I correctly followed the Microsoft example, but obviously I did not and I am having trouble seeing where I departed. Thanks.
OK, I found this wonderful tool called the Office Productivity Tool. It has a code generation feature, so I was able to compare what I was doing with what Word does. Basically the problem was with setting the property value. This snippet does the trick:
Dim cProps As Properties = cPart.Properties
Dim val As DocumentFormat.OpenXml.VariantTypes.VTLPWSTR = New DocumentFormat.OpenXml.VariantTypes.VTLPWSTR
val.Text = tr.ID.ToString
Dim newProp As CustomDocumentProperty = New CustomDocumentProperty() With {
.Name = "TranscriptID",
.FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"
}
newProp.Append(val)
cProps.AppendChild(newProp)
I have to create a Powershell script that reads a Word file (.docx) and replaces strings by hyperlinks. So far, based on this script, I can replace all occurrences of a string by another string easily. With that script, I can look for a string and replace it by an hyperlink. However, only the first occurrence is replaced.
Here's my understanding of the problem so far :
The first script uses the ReplaceWith and Replace=wdReplaceAll parameters of the Execute function of the Find Interface. The issue is that ReplaceWith expects a String and not an Hyperlink object.
The second script doesn't specify those parameters so it only uses the Find.Execute() function to move the start of the Range object to the found string and then insert a link at that position.
Since I can't replace all occurrences at once, I'd try to iterate through all matches to insert links at their location. But Find.Execute() only returns a Boolean... Now I'm thinking of maybe redefining the range to exclude the found occurrence and looping until the end of the doc, but this feels complicated.
Let's say I've got a Word file with this text :
In other words, each of the articles linked here is an index to
multiple lists on a topic. Some of the linked articles are themselves
lists of lists of lists. This article is also a list of lists.
Here's a bare bone script that replace only the first occurrence of "lists" by a relative link. I'm trying to replace all occurrences of "lists" to the hyperlink $linkPath, but can't find how. Help ?
Add-Type -AssemblyName "Microsoft.Office.Interop.Word"
$wdunits = "Microsoft.Office.Interop.Word.wdunits" -as [type]
$objWord = New-Object -ComObject Word.Application
$objWord.Visible = $false
# Text to find and replace by a link
$findText = "lists"
# Link to file
$linkPath = ".\Untitled.png"
# Source Word (2007+) file
$objDoc = $objWord.Documents.Open([FILE TO READ FROM])
# Resulting file
$saveAs = [FILE TO SAVE TO]
# Set Range to all document content
$range = $objDoc.Content
$range.movestart($wdunits::wdword,$range.start) | Out-Null
# Execute params
$matchCase = $false
$matchWholeWord = $true
$matchWildcards = $false
$matchSoundsLike = $false
$matchAllWordForms = $false
$forward = $true
$wrap = 1
$format = $False
$wdReplaceNone = 0
$wdFindContinue = 1
$wdReplaceAll = 2
# $wordFound is true is $findText is found in $range.
# $range.find.execute modifies the start of the range
$wordFound = $range.find.execute($findText,$matchCase,`
$matchWholeWord,$matchWildCards,$matchSoundsLike,`
$matchAllWordForms,$forward,$wrap)
if($wordFound){
$objDoc.Hyperlinks.Add($range,$linkPath,$null,$null,$findText) | Out-Null
}
$objDoc.SaveAs($saveAs)
$objDoc.Close()
$objWord.Quit()
$rc = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($objWord)
[gc]::Collect()
[gc]::WaitForPendingFinalizers()
Reference
Find Interface
Find.Execute
Range Interface
Hyperlinks Object
As with any dataset you have to loop to hit all items in the data set to take action on specific in the dataset. You are not doing this in your code. In MSWord, you need to walk the document. For example I am show code for deletes, but this just as well could be your replace effort.
Example: VBA for just delete any hyper link
Sub RemoveHyperlinksInDoc()
' You need to delete collection members starting from the end going backwards
With ActiveDocument
For i = .Hyperlinks.Count To 1 Step -1
.Hyperlinks(i).Delete
Next
End With
End Sub
Example PowerShell for delete all hyperlinks
Param
(
[string]$Document = $Word.Documents.Open((Read-Host -Prompt 'Enter the full path to the Word document'))
)
$Word = New-Object -ComObject Word.application
$Hyperlinks = #($Document.Hyperlinks)
$hyperlinks | ForEach { $_.Delete() }
$document.save()
$document.Close()
$word.quit()
Example: PowerShell for delete only image hyperlinks
Param
(
[string]$Document = $Word.Documents.Open((Read-Host -Prompt 'Enter the full path to the Word document'))
)
$Word = New-Object -ComObject Word.application
$Hyperlinks = #($Document.Hyperlinks)
$Hyperlinks | ForEach {
If ($_.Shape) {$_.Delete()}
Else {$_.Name;Write-Warning -Message 'Hyperlink is not a graphic and will not be removed'}
}
$Document.save()
$Document.Close()
$Word.quit()
I have more than one instance of Firefox open, multiple tabs in each, and Chrome also running. I wish to be able to find which tab of which browser is running Pandora, for example.
I am working primarily in VBScript, but can tackle PowerShell and might have access to VB.net.
With VBScript you need a third party component to get access to that kind of information. Otherwise your only option is to shell out and parse the output of the tasklist command.
searchTerm = "Pandora"
Set sh = CreateObject("WScript.Shell")
Set ps = sh.Exec("tasklist /v /fo csv")
data = ps.StdOut.ReadAll
searchTerm = "Pandora"
Set re = New RegExp
re.Pattern = "^""(?:chrome|firefox|iexplore|opera)\.exe"""
re.IgnoreCase = True
For Each line In Split(data, vbNewLine)
If re.Test(line) Then
fields = Split(Mid(line, 2, Len(line)-2), """,""")
If InStr(fields(UBound(fields)), searchTerm) > 0 Then
pid = CInt(fields(1))
End If
End If
Next
sh.AppActivate pid
In PowerShell it's a lot easier to do this kind of thing, since Get-Process already provides you with the window titles.
$searchTerm = 'Pandora'
Get-Process | Where-Object { $_.MainWindowTitle -like "*$searchTerm*" }
The following code is part of a function that is being called for each file the program finds during it's search through a directory.
set searchname = objFSO.OpenTextFile(server.mappath(filename),1, true)
do until searchname.AtEndOfStream
lineData = lcase(searchname.ReadLine())
if instr(lineData,s)>0 then
instances = instances + 1
end if
Loop
This the part of my code I'm confused with. This code worked perfectly yesterday. I made a few edits but mostly with HTML and CSS. When I pulled this code out today to optimize it better, I just keep getting a permission denied error with this line.
set searchname = objFSO.OpenTextFile(server.mappath(filename),1, true)
The problem is that the folder the code scans through contains some files that have more than one word. So it's essential that I get the "filename" variable within double quotes. I've been looking everywhere for a solution and none of them have been helpful yet. Is this even possible or is there a workaround?
Any help would be much appreciated here.
EDIT: As requested here's the code that gets the filename. Although no idea how it pertains to my question.
Function filesearch(name)
ext = instr(name,".txt")
vdirectoryvalue = instr(name,"Files\")+6
idname = mid(name,vdirectoryvalue,ext)
vdirectory = len(idname) - 4
filename = left(idname,vdirectory)
instances = 0
set searchname = objFSO.OpenTextFile(server.mappath(idname), 1, true)
do until searchname.AtEndOfStream
lineData = lcase(searchname.ReadLine())
if instr(lineData,s)>0 then
instances = instances + 1
end if
Loop
End Function
What I'm trying to do here would seem to be pretty simple. At the start of my SSIS package I want to set a variable to a directory that the user is prompted for. Here's my VB code:
Public Sub Main()
Dim fldDialog As FolderBrowserDialog = New FolderBrowserDialog
fldDialog.Description = "Select the folder..."
fldDialog.ShowNewFolderButton = False
fldDialog.RootFolder = Environment.SpecialFolder.MyDocuments
If fldDialog.ShowDialog() = DialogResult.OK Then
Dts.Variables("variable").Value = fldDialog.SelectedPath
Dts.TaskResult = ScriptResults.Success
Else
MsgBox("You need to select a folder!", MsgBoxStyle.Exclamation, "Error!")
Dts.TaskResult = ScriptResults.Failure
End If
End Sub
Of course, I've got "variable" set as a "ReadWriteVariables" in the Script Task Editor, and the "Imports System.Windows.Forms" at the top of my VB file.
When I run the task it just sits there yellow (as if it's running), but never shows the dialog. There's never even an error, it just sits there. I can run the same code within a standard windows application project no problem.
Not sure what's going on here. On know one quirk of showing an OpenFileDialog is you have to set the ShowHelp property to True, so I'm wondering if there's another quirk to getting this to run. Google only mostly shows me an old problem where the folder tree is blank, but I'm not even getting the prompt. Any help would be much appreciated.
I know it's a little bit late but I came across this Problem and found a fix for this. (Using C#)
You need to use the "OpenFileDialog" instead of the "FolderBrowserDialog" and you need to set a few adjustments. Here is a sample code which opens the explorer and lets you pick a folder:
public void Main()
{
string myPath="";
OpenFileDialog folderBrowser = new OpenFileDialog();
folderBrowser.ValidateNames = false;
folderBrowser.CheckFileExists = false;
folderBrowser.CheckPathExists = true;
folderBrowser.FileName = "Folder Selection.";
folderBrowser.ShowHelp = true;
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
myPath = Path.GetDirectoryName(folderBrowser.FileName);
}
else
{
MessageBox.Show("Error selecting path");
}
Dts.Variables["User::varFolderPath"].Value = myPath;
Dts.TaskResult = (int)ScriptResults.Success;
}
The most important statement is the "folderBrowser.ShowHelp = true" statement. If this assignment isn't made you'll get the same problem as in your question.
You also need the statements above to "trick" the Dialog so you can select a Folder instead of a File.
I hope I can help you or people with the same problem but you should pass in the folder as a variable into the package as "N West" said.