patterncsharpMinor
Exporting a UserForm from the VBE... and working around API bugs
Viewed 0 times
aroundtheexportinguserformworkingvbebugsandfromapi
Problem
Rubberduck has been using an extension method to export code files, notably for source control purposes.
While testing source control, I ran into a weird bug in the VBIDE API, which caused UserForm modules to perpetually be treated as "modified", because every time we exported them, the VBIDE API would add an empty line at the beginning of the code-behind.
So I had to modify our
Modifying the file directly in the export folder wasn't an option, because then Git would have considered it as modified anyway.
For context/reference, here's what a UserForm code file looks like:
Notice the
Here's the extension method in question:
```
///
/// Exports the component to the directoryPath. The file is name matches the component name and file extension is based on the component's type.
///
/// The component to be exported to the file system.
/// Destination Path for the resulting source file.
public static string ExportAsSourceFile(this VBComponent component, string directoryPath)
{
var path = Path.Combine(directoryPath, component.Na
While testing source control, I ran into a weird bug in the VBIDE API, which caused UserForm modules to perpetually be treated as "modified", because every time we exported them, the VBIDE API would add an empty line at the beginning of the code-behind.
So I had to modify our
ExportAsSourceFile extension method to work around that glitch:- Export the form to a temporary file
- Read the contents of the file
- Locate the last line of the file heading (attributes that are now showing in the VBE)
- Determine if we have an extraneous empty line, and remove it
- Write the modified content into the intended export folder
Modifying the file directly in the export folder wasn't an option, because then Git would have considered it as modified anyway.
For context/reference, here's what a UserForm code file looks like:
VERSION 5.00
Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} UserForm1
Caption = "UserForm1"
ClientHeight = 3165
ClientLeft = 45
ClientTop = 390
ClientWidth = 4710
OleObjectBlob = "UserForm1.frx":0000
StartUpPosition = 1 'CenterOwner
End
Attribute VB_Name = "UserForm1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Option Explicit 'first line of actual user code visible in the VBE
Notice the
VB_PredeclaredId attribute is always the last one specified: my fix relies on that detail.Here's the extension method in question:
```
///
/// Exports the component to the directoryPath. The file is name matches the component name and file extension is based on the component's type.
///
/// The component to be exported to the file system.
/// Destination Path for the resulting source file.
public static string ExportAsSourceFile(this VBComponent component, string directoryPath)
{
var path = Path.Combine(directoryPath, component.Na
Solution
Well, here's a form saved from Excel 2013.
Notice the VB_Exposed, VB_HelpID and VB_Description attributes. I set the Description and Help attributes using the Object Browser Context Menu ("Properties"), but a user might have manually edited the attributes in a previous export/import, and so they might be in a different order
There is a trailing blank line, but CR MarkDown isn't showing it.
Edit:
With Excel working again, I imported the file, and I do get the extra header line, and if it is exported with a header line, and reimported, there are then 2 header lines, so I'm getting the same behaviour as you.
You won't be able to rely on the position/order of VB_Attributes, so you'll have to parse the file.
You're also assuming a user won't have intentionally placed a blank line at the top of the module. And what happens if the user has more than one blank line at the top of the module?
The safest approach would probably be to count the blank lines at the top of the code module, before the export, and then adjust the number of blank lines found after the last VB_Attribute but before the actual code begins.
That would potentially deal with any fix that might come to VBE.
Notice the VB_Exposed, VB_HelpID and VB_Description attributes. I set the Description and Help attributes using the Object Browser Context Menu ("Properties"), but a user might have manually edited the attributes in a previous export/import, and so they might be in a different order
VERSION 5.00
Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} UserForm1
Caption = "UserForm1"
ClientHeight = 3165
ClientLeft = 45
ClientTop = 390
ClientWidth = 4710
OleObjectBlob = "UserForm1.frx":0000
StartUpPosition = 1 'CenterOwner
End
Attribute VB_Name = "UserForm1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Attribute VB_HelpID = 55
Attribute VB_Description = "This is a description"
Private Sub UserForm_Click()
Debug.Print "Test"
End SubThere is a trailing blank line, but CR MarkDown isn't showing it.
Edit:
With Excel working again, I imported the file, and I do get the extra header line, and if it is exported with a header line, and reimported, there are then 2 header lines, so I'm getting the same behaviour as you.
You won't be able to rely on the position/order of VB_Attributes, so you'll have to parse the file.
You're also assuming a user won't have intentionally placed a blank line at the top of the module. And what happens if the user has more than one blank line at the top of the module?
The safest approach would probably be to count the blank lines at the top of the code module, before the export, and then adjust the number of blank lines found after the last VB_Attribute but before the actual code begins.
That would potentially deal with any fix that might come to VBE.
Code Snippets
VERSION 5.00
Begin {C62A69F0-16DC-11CE-9E98-00AA00574A4F} UserForm1
Caption = "UserForm1"
ClientHeight = 3165
ClientLeft = 45
ClientTop = 390
ClientWidth = 4710
OleObjectBlob = "UserForm1.frx":0000
StartUpPosition = 1 'CenterOwner
End
Attribute VB_Name = "UserForm1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Attribute VB_HelpID = 55
Attribute VB_Description = "This is a description"
Private Sub UserForm_Click()
Debug.Print "Test"
End SubContext
StackExchange Code Review Q#123400, answer score: 6
Revisions (0)
No revisions yet.