I'm following this mail merge work that I found in a related Stack Exchange question which looks to do something similar to what I'd like but I'm having trouble getting started. I"ve narrowed the problem down to the below...
Sub TestEmailer()
Dim Source As Document, Maillist As Document, TempDoc As Document
End Sub
With the error "User-defined type not defined."
It appears I'm missing a reference, but the reference recommended "Microsoft Office 16.0 Object Library" doesn't seem to fix the issue when I enable it.
Has this Document object moved to a different library? How do I find the right library in the future?
If you're doing this in Excel, you need to add a reference to Microsoft Word 16.0 Object Library and then use:
Sub TestEmailer()
Dim Source As Word.Document, Maillist As Word.Document, TempDoc As Word.Document
End Sub
Each application will only load it's own objects by default, along with other common Office objects. If you want to use other objects from another application you either need to add a reference to that application's object library or use late binding.
In this instance I'd recommend early binding (adding a reference manually) so that you get the benefit of IntelliSense in your code, this will make it easier working across applications.
Related
I've searched the forums for something similar to this, but most of the issues I've seen do not have access to the variables in the debugger and have issues with casting the object.
I'm running a C# application which calls a VB class called wrapper.cs, which uses MS Word objects. This code was tried and tested and worked fine with .NET 4.8 but now breaks in .NET 5.
I have a variable: Protected m_wdApp As Object
When the class is instantiated, it calls:
Public Sub New()
m_wdApp = CreateObject("Word.Application")
m_wdApp.Visible = False
m_outputFormat = DOC_FORMAT
End Sub
This throws the following exception:
Now the above would be all very fine and well, if I wasn't able to actually look at the contents of the m_wrdApp and see that yes, they are all intact and are all visible in my watch Windows.
Using the Immediate window, attempting to access any of these attributes from the Word object throws an error (as you'd expect).
However, if I ask VS to "add a watch" to one of the attributes, for instance this "Visible" which we currently need, it comes out with this convoluted watch name:
I should note that all of this worked fine before upgrading to .NET 5, so I'm wondering if this is either a bug or there are some new rules for dealing with late bound variables.
As #dbasnett and #charlieface mentioned above, we should be using Interop.
We added Imports Microsoft.Office.Interop
We also changed
Protected m_wdApp As Object
to...
Protected m_wdApp As Word.Application
And needed to update variables such as
Dim inlineShape As Object
to
Dim inlineShape As Word.InlineShape
and...
For Each hl As Object In m_wdApp.ActiveDocument.Hyperlinks
to:
For Each hl As Word.Hyperlink In m_wdApp.ActiveDocument.Hyperlinks
I couldn't see why moving away from late binding would impact us and I'm assuming the move the Interop is in line with .NET 5 coding guidelines, with their many improvements in Interop code in recent years.
Many thanks for this tip guys!
Whatever way one opens Word (Excel etc.) I aim that the instance is 'safe' and independent. What I mean by that is that if the user currently has a Word window open that this doesn't interact with what my software is opening and that both windows can operate independent of one another.
The best way to describe this is the effect of right-clicking on Word or Excel in the taskbar and opening up a new Word (or Excel) window (as opposed to opening a file). Each time you do this, you seem to get a complete and independent instance.
It would seem that the following code:
Dim wdApp As Word.Application
Dim oDoc As Word.Document
wdApp = CreateObject("Word.Application")
... will open up within an existing window if Word is currently running on the PC. How do I force a fresh and independent instance of Word?
Using New Word.Application is preferred if the project has a reference to the Word APIs. Based on the sample code in the question, that would be the case in this instance.
Another possibility would be to create a new Process and hook up to that. In this case, a reference to the Word APIs would not be necessary (late-binding).
Dim wdApp As Word.Application = New Word.Application()
Dim oDoc As Word.Document = wdApp.Documents.Add() 'or wdApp.Documents.Open(filepath)
CreateObject should not be using an existing window, only GetObject would do that. Assuming the assertion in the question cannot be confirmed (I've never seen it happen, in over thirty years), then CreateObject is also possible for starting a new instance of an Office application. This would also not require a reference to the Word APIs.
As to the topic of maintaining an independent instance of an Office application: You may also want to refer to this SO answer.
I wrote a powerpoint add-in that works well on my local computer. Yet when I email the file to others, all of a sudden half the functionalities no longer work and show the compile error as listed in the subject line.
Digging a bit deeper, it appears the issue is that the client computers do not have the correct reference libraries (e.g., Excel 14.0 Object Library, Outlook, Access, etc.). I have these libraries correctly referenced when writing the add-in as a pptm file, but imagine that when I saved the pptm into a ppam file, the reference libraries were "lost" somehow.
Does anyone have any ideas? Note that adding the reference libraries directly on the client computers does not appear to help. Instead, I'd need to send the client the foundational pptm file, add the reference libraries, then save that file as a ppam file on the client computer directly, which of course is not practical. So how does one "embed" reference libraries to the add-in?
Thank you!
So how does one "embed" reference libraries to the add-in?
You don't.
Alternative:
Instead of Early binding, use Late Binding.
Example code of Early Binding.
Here we set a reference to MS Word xx.xx Object Library.
Sub Sample()
Dim oWrd As Word.Application
Dim oDoc As Word.Document
Set oWrd = New Word.Application
Set oDoc = oWrd.Documents.Open("....")
'
'~~> Rest of the code
'
End Sub
Converting the above to Late Binding.
Here we do not set the reference but let the code bind to whatever version of MS Word is present in the destination pc.
Sub Sample()
Dim oWrd As Object
Dim oDoc As Object
Set oWrd = CreateObject("Word.Application")
Set oDoc = oWrd.Documents.Open("....")
'
'~~> Rest of the code
'
End Sub
Similarly you can change your code for MS-Excel as well.
For Further Reading
I have a tried reading an embedded excel document in a word document. I followed the code specified at this blog article:
http://vbadud.blogspot.com/2010/08/how-to-read-excel-sheet-embedded-in.html
Dim oWB As Excel.Workbook
Dim oIShape As InlineShape
For Each oIShape In ActiveDocument.InlineShapes
If InStr(1, oIShape.OLEFormat.ProgID, "Excel") Then
oIShape.OLEFormat.Activate
Set oWB = oIShape.OLEFormat.Object
oWB.Sheets(1).Range("A1").Value = "ProdID"
End If
Next oIShape
It works fine but the Activate line causes the document to flicker on each excel document I read. I tried to remove the oIShape.OLEFormat.Activate code but it causes the next line to throw a "Runtime error '430' (class does not support Automation or does not support expect).
The question is there any other way to access embedded excel without calling the Activate method?
This is tricky! The short answer is, no. Not with an embedded Excel.
I did some experimentation and some research. Since I could not find any sources that specifically explained the behavior. this is somewhat a guess on my part. It appears that when you embed the Excel spreadsheet into your word document essentially Word stores a link of spreadsheet, which displays only the appearance because it needs to be interpreted with the Excel program. Until you actually active the shape, you cannot interact with it because that cannot be done with Word directly. This article alludes to the behavior, but doesn't explain it. Here's a quote:
If you edit the object in Word, click anywhere outside the object to return
to the destination file.
If you edit the object in the source program in a separate window,
click Exit on the File menu of the source program to return to the
destination file.
You may have noticed that even if you use. Application.ScreenUpdating = false it still does the flickering you mention. This is because you are using a different application when you access the shapes! Every time you active the shape, the object specific menus etc load.
A possible work around:
If instead of embedding Excel Spreadsheets via the insert menu, you can instead add a control. On my machine using Office 2003 the comparible one is: Microsoft Office Spreadsheet 11.0 This is technically a web control, but the methods and behavior are very comparable to an Excel workbook.
Using the control instead of the handy inserted object, with a slight variation of your code I was able to comment out your activate command and the code ran as expected. Specifically, I had to change these lines:
Dim oWB As Spreadsheet instead of Excel.Workbook.
If InStr(1, oIShape.OLEFormat.ProgID, "OWC11.Spreadsheet.11") Then instead of "Excel"
Basically you can decide... Activate your embedded object that requires Excel to interpret, or use a different control that doesn't require activation.
I can do this:
Dim fso As New FileSystemObject
or I can do this:
Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")
How do I know what string to use for CreateObject? For example, how would I know to use the "Scripting." part of "Scripting.FileSystemObject"? Where do you go to look that up?
It is the ProgID of the component which is registered in Windows registry under HKCR key:
HKEY_CLASSES_ROOT\Scripting.FileSystemObject
ProgID's are human readable identifiers for COM objects. They point to the actual CLSIDs, which in this case is:
HKEY_CLASSES_ROOT\CLSID\{0D43FE01-F093-11CF-8940-00A0C9054228}
This is the place where you can find the actual COM .dll that includes the implementation of the component.
In the first sample code you have provided you are doing an early-binding, and in the second one you are doing a late-binding.
Using the VB6 IDE, choose Project, References, then to pick the reference 'Microsoft Scripting Runtime'.
If you didn't know what the reference is called, you could use the References dialog's Browse button to pick the file /system 32/scrrun.dll.
With the reference chosen, close the References dialog then open the Object Browser (View menu). Change the dropdown to the most likely candidate, being 'Scripting'. This will reveal the library's classes, one of which is 'FileSystemObject'. Hence, you will have discovered the the string required for CreateObject is 'Scripting.FileSystemObject'.
If you didn't know the Reference name or the file name but you did know the class name then you could search the registry for "FileSystemObject" and it should soon be revealed that the fully-qualified name you require is 'Scripting.FileSystemObject'.
I would start by searching for FileSystemObject in the MSDN library at http://msdn.microsoft.com/library
The site is chock full of documentation, including the details of how to call CreateObject.