Insert data from registry into predefined bookmarks - vbscript

I've got data in the registry under Current User which I want to add into predefined bookmarks in Word documents. Now, the thing is, I want the exact same macro in every single Word document and have the macro skip the step if it can't find the predefined bookmark.
I've got this code already where I need to predefine an array of the total data I've got in the registry. The macro stores the values of each subkey from the registry into a string. But doesn't insert the value into the bookmark unless the bookmark exists. Well, that's the idea anyway.
Public Sub TemplateData()
Dim objShell
Dim strShell
Dim strDataArea
Dim Values() As String
Dim Fields
' Input the exact same key as in registry
Fields = Array("DEPARTMENT", "LETTER", "LNAME", "FNAME")
Set objShell = CreateObject("Wscript.Shell")
strDataArea = "HKCU\Templates\"
On Error Resume Next
For iTeller = 0 To UBound(Fields)
Dim sBookMarkName, sValue
sBookMarkName = "Bookmark" & Fields(iTeller)
sValue = objShell.RegRead(strDataArea & Fields(iTeller))
Selection.GoTo What:=wdGoToBookmark, Name:=sBookMarkName
Selection.Delete Unit:=wdCharacter, Count:=0
Selection.InsertAfter sValue
Next
On Error GoTo 0
End Sub
If I put a breakpoint on the bottom Next, I can see that it indeed finds the bookmarks, but somehow it inserts the values of every registry subkey until it finds the correct one. And the initial idea is also to have the macro delete whatever words are in the bookmkarks if I run the macro again since if the data in the registry get updates, the macro should pick that up and insert it into the document.
Can anyone find what might be wrong here?

Error in a Word VBA macro, trying to insert values into bookmarks
has answers. Kenny learned himself (re-set the Bookmark):
ActiveDocument.Bookmarks.Add Range:=Selection.Range, Name:=sBookMarkName
and Tom helped to check before use...
If ActiveDocument.Bookmarks.Exists(sBookmarkName) Then
... insert using your code
End If

Related

Iterate over pre-filtered list of folders

I'm working with a client's flat folder structure that has a single folder containing 45k subfolders with 8-digit folder names, e.g. 51023231. I have a small script that ripples through them and copies them into a bin-sorted set of subfolders elsewhere on the network (for use with SharePoint), such that the first 5 digits are used as a parent folder, i.e. the contents of 51023231 are copied into 51023\51023231. It works perfectly well, and I've managed to some modest optimisation when dealing with folders new to the destination.
However, it can take an hour or so to run over the entire 45k set of folders doing folder by folder comparisons, I was wondering if it was possible to run a system-level query to return only an initial list of folders whose modification dates were after a given point, and then run the existing script over that. I've done the usual Google-is-your-friend type trawl, and keep hitting the idea of using WMI to do this, but I don't get much further. Is this because it simply isn't possible with VBScript?
Any pointers gratefully received.
It's possible with VBScript using WMI. It's not something you'd be able to do using the FileSystemObject, if that's what you're implying.
You can query WMI's Win32_Directory class to filter folders by modified date. The only tricky part is the datetime format used by WMI. But the SWbemDateTime class can convert a VBScript date to a datetime value.
Here's an example:
' Create a datetime value for use in our WMI query...
Dim dt
Set dt = CreateObject("WbemScripting.SWbemDateTime")
dt.SetVarDate DateSerial(2015, 8, 31)
Dim objWMI
Set objWMI = GetObject("winmgmts:\\.\root\cimv2")
' Find all subfolders of 'c:\some\path' that were modified > 2015/08/31
Dim objFolders
Set objFolders = objWMI.ExecQuery("select * from Win32_Directory" _
& " where Drive='C:' and Path='\\some\\path\\' and LastModified>'" & dt & "'")
Dim objFolder
For Each objFolder in objFolders
WScript.Echo objFolder.Name
Next

VBA - Export individual PowerPoint slides into PDF files, named by content field

I have about 15 different slide decks, that contain 3-4 slides each. Each deck is comprised of "certificates" to be broken out and given to each individual recipient, who is named on the certificate. This process is repeated every month.
ideally, I would like to enable a VBA script to extract each sheet to save as (or print as) a PDF, and save the file with the name of the recipient named on the slide... is this possible?
The best I have found is this code, (which doesn't work on my mac, but works fine on a VM) which DOES separate the slides into PDF's, but I would like the naming to be changed.
Any help would be greatly appreciated!!
Sub ExportSlidesToIndividualPDF()
Dim oPPT As Presentation, oSlide As Slide
Dim sPath As String, sExt As String
Set oPPT = ActivePresentation
sPath = oPPT.FullName & "_Slide_"
sExt = ".pdf"
For Each oSlide In oPPT.Slides
i = oSlide.SlideNumber
oSlide.Select
oPPT.ExportAsFixedFormat _
Path:=sPath & i & sExt, _
FixedFormatType:=ppFixedFormatTypePDF, _
RangeType:=ppPrintSelection
Next
Set oPPT = Nothing
End Sub
Edit for further clarification:
I'm forced to run the script in my Windows Virtual Machine, and when I do that, I'm losing all of my formatting that I set up in my slides, created in office '11 (which is one of my big reasons to export to PDF in the first place).
A little more about my project:
1. I am compiling a couple thousand lines of "actions" (data), to determine a winner for a recognition award, based on criteria for 4 different award categories, for 12-15 different executives.
2. Results are tallied, then I retrieve the winners information (name, ID, and the same for their manager and executive) from an LDAP directory. Then put into a csv for mail-merge at later step.
3. A Template slide deck is used for all slides, (12-15 executives have 3-4 winners each).
4. Individual PDF certificates are created from those slides.
5. Congratulatory message is drafted, fields pulled from prepared csv for mail-merge, and then exported to outlook 2011 outbox (client offline).
6. I attach each individual certificate to each message manually in the outbox.
7. Send.
I have compiled sample set of the files linked here: Recognition Files (Sample)
On each slide there are one or several shapes. For the sake of illustration, assume that the recipient's name is in the first shape.
Sub ExportSlidesToIndividualPDF()
Dim oPPT As Presentation, oSlide As Slide
Dim sPath As String, sExt As String
Set oPPT = ActivePresentation
sExt = ".pdf"
For Each oSlide In oPPT.Slides
'## Retrieve the recipient's name from the shape and append it to the exported path:
sPath = oPPT.FullName & oSlide.Shapes(1).TextFrame.TextRange.Text
i = oSlide.SlideNumber
oSlide.Select
oPPT.ExportAsFixedFormat _
Path:=sPath & sExt, _
FixedFormatType:=ppFixedFormatTypePDF, _
RangeType:=ppPrintSelection
Next
Set oPPT = Nothing
End Sub
It may be more complicated, depending on how the slides are configured (e.g., if there is a shape that contains the name and other text, you will need a function to parse out the proper name and omit the rest, etc.
Provide more detail, about the problem if you need further assistance.

how to get email address from messages in outlook 2003

Sub GetALLEmailAddresses()
Dim objFolder As Folders
Set objFolder = Application.ActiveExplorer.Selection
Dim dic As New Dictionary
Dim strEmail As String
Dim strEmails As String
Dim objItem As MailItem
For Each objItem In objFolder.Items
strEmail = objItem.SenderEmailAddress
If Not dic.Exists(strEmail) Then
strEmails = strEmails + strEmail + ";"
dic.Add strEmail, ""
End If
Next
Debug.Print strEmails
End Sub
I use this code to get email address from message body. I'm not prefect in vb. is there any to help how to get email address from messages in outlook 2003?
In that case, I don't think there's anything built in, so I'd suggest that you don't bother with the SenderEmailAddress but instead just get out the Body and then search the text for email addresses. This will get somewhat complicated though since it might be difficult to be able to tell what's part of an email address and what isn't.
The easiest place to start with is to just look for any # in the text, and then search for the next whitespace on either side of the # and get everything between those whitespaces. But there are a lot of issues to think about. What if the user typed # for some other reason, or if the email contains something like The first email is xxx#test.com.The second email is xxx2#test.com (note the missing space between the . and the The), where your app might think that the email should be xxx#test.com.The.
Edited since my answer was based on a complete misunderstanding of the question.

VB6 control iteration: one control gives me an error; which one?

I am trying to loop through all controls in a form:
For Each ctrl in Me.Controls
in order enable/disable the control based on some conditions.
But there is a control on the form that gives an error when I try to access it. What kind of control does that, and how do I find it?
When you get your error and click Debug, is the error on the line setting a control's Enabled property?
If so, add a Debug.Print statement writing out the control's name. Do so on the line before setting the Enabled property.
Here's what I mean:
Dim ctrl As Control
For Each ctrl In Me.Controls
Debug.Print ctrl.Name
ctrl.Enabled = True
Next
The Debug.Print statement will write out to the Immediate Window the name of the control that was last processed in the loop, presumably the one that caused your error.
EDIT
This might work. Put this control in a Panel control and set the Panel's Enabled property to False. If I recall correctly, in VB6 setting a container control's Enabled property to False will also set the container's child controls Enabled to False. If your control's Enabled property really is read-only, I'm curious what would happen.
Try this:
Dim ctr As Control
Dim CtrStatus Boolean
CtrStatus = False
For Each ctr In Me.Controls
If (SSTab.hwnd = GetParent(ctr.hwnd)) Then
Call CallByName(ctr, "Enabled", VbLet, CtrStatus)
else
ctr.Enabled = CtrStatus
End If
Next
Another approach is as follows, that also works at runtime (as opposed to just in the IDE):
private sub SetEnabled()
on error goto errHandle
Dim ctrl As Control
For Each ctrl In Me.Controls
ctrl.Enabled = True
Next
exitPoint:
exit sub
errHandle:
MsgBox "Error " & err.Description & " with Control " & ctrl.Name
resume exitPoint
end sub
Suppress the error reports before the loop and then set it back to standard error handling:
On Error Resume Next
For Each ctrl In Me.Controls
ctrl.Enabled = lIsEnabled
Next
On Error GoTo 0
OR name your controls with a standard prefix/suffix that you can check by name and skip in the loop.
For Each ctrl In Me.Controls
If Left(ctrl.Name, 3) = "sst" Then
ctrl.Enabled = lIsEnabled
End If
Next
Tosa: from your comment on AngryHacker's answer, I think you are checking the container incorrectly.
Your code is like this
' BAD CODE '
If ctrl.Container = fraMovies Then
For me that gives error 450 Wrong number of arguments or invalid property assignment. Do you get the same error?
The code should use Is rather than =, like this
' GOOD CODE '
If ctrl.Container Is fraMovies Then
Explanation. You want to check whether two variables "point" to the same control. Controls are objects: you must use Is not = to check whether two object variables "point" to the same object. This is a classic pitfall in VB6.
One last word. Next time, could you try to post 10 lines or less of actual code, reproducing the error, and give the exact error number and message and the exact line on which it occurs? It really does make it much easier for us to solve your problem - I know it's work for you to shorten the code, but you'll get better answers that way.
EDIT Welcome back! :) You said some controls don't have a Container property. You could try wrapping the test in On Error Resume Next, something like this.
' GOOD CODE '
Dim bMatch As Boolean
Dim ctrl As Control
For Each ctrl In Me.Controls
bMatch = False
On Error Resume Next
bMatch = (ctrl.Container Is fraMovies)
On Error Goto 0
If bMatch Then
ctrl.Enabled = True
End If
Next
To avoid such problems follow the given rules while naming contols
When you name an element in your Visual Basic application, the first character of that name must be an alphabetic character or an underscore.
**Begin each separate word in a name with a capital letter, as in FindLastRecord and RedrawMyForm.
Begin function and method names with a verb, as in InitNameArray or CloseDialog.
Begin class, structure, module, and property names with a noun, as in EmployeeName or CarAccessory.
Begin interface names with the prefix "I", followed by a noun or a noun phrase, like IComponent, or with an adjective describing the interface's behavior, like IPersistable. Do not use the underscore, and use abbreviations sparingly, because abbreviations can cause confusion.
Begin event handler names with a noun describing the type of event followed by the "EventHandler" suffix, as in "MouseEventHandler".
In names of event argument classes, include the "EventArgs" suffix.
If an event has a concept of "before" or "after," use a suffix in present or past tense, as in "ControlAdd" or "ControlAdded".
For long or frequently used terms, use abbreviations to keep name lengths reasonable, for example, "HTML", instead of "Hypertext Markup Language". In general, variable names greater than 32 characters are difficult to read on a monitor set to a low resolution. Also, make sure your abbreviations are consistent throughout the entire application. Randomly switching in a project between "HTML" and "Hypertext Markup Language" can lead to confusion.
Avoid using names in an inner scope that are the same as names in an outer scope. Errors can result if the wrong variable is accessed. If a conflict occurs between a variable and the keyword of the same name, you must identify the keyword by preceding it with the appropriate type library. For example, if you have a variable called Date, you can use the intrinsic Date function only by calling DateTime.Date.

Save images in Outlook 2007

Programmatically, of course.
Having already asked this question on superuser, I'm looking at writing a simple macro to pull down the displayed image in an HTML message (email or feed) in outlook 2007, and allow me to save it to disk.
Unfortunately, I havent been able to find where in the OL object model I can reference either linked images, or the html content itself. Finding attached files is easy, its the linked/displayed images that is my issue.
Any help? Of course, if you have a better non-programmatic answer, I'll be glad to see that - over on superuser, of course...
This is based on the MSDN docs. I don't have Outlook to test it.
Assuming you have an email message open, you can call GetInspector method on the MailItem instance that you have & use its HTMLEditor property to get handle to the DOM.
From here on, you can call regular methods such as document.Images to get handle to all the image elements. I don't know, how one can save it locally to a disk but I am sure, there must be some method to do it.
I had a second look at shahkalpeshs answer and came up with the following solution:
(Written in Outlook 2003)
Option Explicit
Private Sub getImages()
Dim xmlhttp_ As xmlhttp
Dim htmldoc As Object
Dim currentImage As Object
Dim currentResponse() As Byte
Dim startTime As Date
Dim maxTime As Long
Dim pathFolder As String
Dim pathFull As String
Dim nrFile As Integer
pathFolder = "C:\YourFolder\Images\" '"(small fix for stackoverflow syntaxhighlighter)
maxTime = 30 ' max time to load 1 File in seconds '
If Me.ActiveWindow.CurrentItem.GetInspector.EditorType = olEditorHTML Then
Set htmldoc = Me.ActiveWindow.CurrentItem.GetInspector.HTMLEditor
Set xmlhttp_ = New xmlhttp
For Each currentImage In htmldoc.images
xmlhttp_.Open "GET", currentImage.src
If Left(currentImage.src, 8) <> "BLOCKED:" Then
xmlhttp_.Send
startTime = Now
pathFull = pathFolder & currentImage.nameProp
pathFull = Replace(pathFull, "?", vbNullString)
pathFull = Replace(pathFull, "&", vbNullString)
Do While xmlhttp_.readyState <> 4
If DateTime.DateDiff("s", startTime, Now) > maxTime Then Exit Do
DoEvents
Loop
If xmlhttp_.readyState = 4 Then
If Dir(pathFull) <> "" Then Kill pathFull
nrFile = freeFile
Open pathFull For Binary As #nrFile
currentResponse = xmlhttp_.responseBody
Put #nrFile, , currentResponse
Close #nrFile
End If
End If
Next currentImage
End If
Set xmlhttp_ = Nothing
Set currentImage = Nothing
Set htmldoc = Nothing
End Sub
This code will download all the images that are displayed in your ActiveWindow and save them in a folder.
You will need to add a Reference to Microsoft XML (any version >= 2.6 should work) through Tools->References in the VBA Editor
If you want to, you can also set a Reference to the Microsoft HTML Object Library and change:
Dim htmldoc As Object
Dim currentImage As Object
to:
Dim htmldoc As HTMLDocument
Dim currentImage As HTMLImg
Regarding your comment:
#marg, Thanks for the detailed response. I still cant believe the solution has to be so convoluted - the image is already displaying, why should I have to download it again? And what if I want to save only a single image? (In Outlook 2003, you were able to right click on the image and select save as... now no more.) Since this is the closes to an actual workable solution, and since there doesnt seem to be any better solution in current Outlook - i'm giving you the bounty...
I don't have 2007 to look for a non-programming solution.
I do not believe that the MailItem Object (CurrentItemin my solution is a MailItem) differs much between the versions (but I base that assumption on 0 % research :D) and I was not able to locate the direct local path where the displayed images are stored even though I am pretty sure they should be in your browsers cache-folder. Crawling your folder for a file with the name currentImage.nameProp and copying it to your destination folder would be an alternative solution. Simply redownloading the image should not be that bad :D

Resources