Recent Entries 10
- pattern minor 112d agoSAFEARRAY export from a C++ DLL to VBAMy code is not very elegant, but I'm a total newbie in c++ and I consider it already a major achievement that I managed to write something that actually works. As background, I'm defining in VBA the function below, contained in "FieldTrans.dll": ``` Private Declare Function ShannonInterp Lib "[Here the name of my path]\FieldTrans.dll" _ (ByVal Matrix As Variant, ByVal MatrixHeight As Integer, ByVal MatrixWidth As Integer, ByVal Res As Double, ByVal DeltaTheta As Double, ByVal DeltaPhi As Double) As Variant ``` and using it by having: ``` Dim InterpCo As Variant InterpCo = ShannonInterp(AmplCo, PointsTheta, PointsPhi, Resolution, 181 / (PointsTheta+1), 361 / (PointsPhi+1)) ``` I thought it'd work like a charm, but it doesn't (yet). Meanwhile I'd like more improvement on the execution time. Therefore the only thing I can optimize is the c++ code contained in the dll, which is: ``` // FieldTrans.h #ifdef FIELDTRANS_EXPORTS #define FIELDTRANS_API __declspec(dllexport) #else #define FIELDTRANS_API __declspec(dllimport) #endif #include // Returns the normalized cardinal sinus coefficient FIELDTRANS_API double _stdcall SinC(double x); extern "C" { // Returns the interpolated field matrix: 180° x 360° with Res step FIELDTRANS_API VARIANT _stdcall ShannonInterp(VARIANT Matrix, int MatrixHeight, int MatrixWidth, double Res, double DeltaTheta, double DeltaPhi); }; // FieldTrans.cpp #include "stdafx.h" #include "FieldTrans.h" #include #include #include double _stdcall SinC(double x) { static const double PI = 3.14159265358979323846; double y; y = 1; if (x != 0) y = sin(PI * x) / (PI * x); return y; } VARIANT _stdcall ShannonInterp(VARIANT Matrix, int MatrixHeight, int MatrixWidth, double Res, double DeltaTheta, double DeltaPhi) { int k, l, m, n; double iRes, jRes; double ThetaSinC, PhiSinC; int Height = static_cast (180 / Res + 1); int Width = static_cast (360 / Res + 1); k = 0; m = 1; long indi[2];
- pattern minor 112d agoAutomating the download of a GitHub repoThis script is designed to locate and download the first repository in your sidebar on the GitHub repository page. It downloads the .zip file for that repo and saves it to the default location. I built this script as a test to determine what it would take to perform this type of operation since I needed to use it on something else, but this script itself is the actual script I use (very) infrequently. Basically, any/all PowerShell idioms would be awesome to have feedback on. The comments are there for the person I was writing the script as an example for, but again this is the script I'm worried about. I want to continually improve it. ``` # This method will finish an IE download by forcing the 'ALT+S' key several times to ensure that the "Save" button gets # hit. It then waits a specified amount of time before returning so that the user can allow the file download to be # expected to have finished. Function Finish-Download([__ComObject]$ie, [int]$iterations, [int]$sleep) { Write-Verbose "Finish-Download: Entered 'Finish-Download ie iterations'" Write-Debug "Finish-Download: 'iterations': $iterations" Write-Debug ("Finish-Download: 'ie.HWND': " + $ie.HWND) Write-Warning "Please do not make any mouse or keyboard interactions for this next step." Write-Verbose "Finish-Download: Locating 'ie' process" $ieProc = Get-Process | Where-Object { $_.Name -eq "iexplore" -and $_.MainWindowHandle -eq $ie.HWND } Write-Verbose "Finish-Download: Attaching 'WScript.Shell' to 'ieProc'" Write-Debug ("Finish-Download: 'ieProc.Id': " + $ieProc.Id) $wScript = New-Object -ComObject WScript.Shell if (-not $wScript.AppActivate($ieProc.Id)) { Write-Error "Failed to attach WScript.Shell to appropriate process." Return $false } # For SOME reason, WScript takes a moment to activate the process. So we need to sleep. During this part it's # important that you don't make any mouse/keyboard movements. # Another hiccup: I
- pattern minor 112d agoProperly destroying the VBEThe disposable wrappers worked exactly as intended... and that turned out being a huge mistake: That's because the .net runtime creates a Runtime Callable Wrapper per-type, not per-instance*; it took a number of refactorings and adjustments just to be able to get the project to build and run, and then over 900 tests were broken and my life was a nightmare. Then I had an idea: since .net wouldn't let me destroy objects that were otherwise ready to be collected, what if I ditched `IDisposable` and moved on to a hierarchical teardown strategy? Enter `ISafeComWrapper`: ``` namespace Rubberduck.VBEditor.SafeComWrappers { public interface ISafeComWrapper { /// /// Releases all COM objects. /// void Release(); } } ``` In the `Extension` class (which you may recall seeing here), we `Release` the entire VBE object graph, like this: ``` private void ShutdownAddIn() { if (_app != null) { _app.Shutdown(); _app = null; } if (_kernel != null) { _kernel.Dispose(); _kernel = null; } _ide.Release(); _isInitialized = false; } ``` So here's the `SafeComWrapper` base class: ``` using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace Rubberduck.VBEditor.SafeComWrappers { public abstract class SafeComWrapper : ISafeComWrapper, IEquatable> where T : class { protected SafeComWrapper(T comObject) { _comObject = comObject; } public abstract void Release(); private readonly T _comObject; public T ComObject { get { return _comObject; } } public bool IsWrappingNullReference { get { return _comObject == null; } } protected TResult InvokeResult(Func member) { try { return member.Invoke(); } catch (COMException exception) { throw new Wrapper
- pattern minor 112d agoA generic IEnumerator to enumerate COM collectionsAs I wrapped the VBIDE API, I encountered a number of "collection types" (`Windows`, `CodePanes`, `VBComponents`, `VBProjects`, `References`, etc.) - these types implement the non-generic `IEnumerable` interface, so if I wanted to be able to iterate my wrappers like this for example: ``` using (var projects = _vbe.VBProjects) { foreach (var project in projects.Where(p => p.Protection == ProjectProtection.Unprotected)) { yield return project.Name; } } ``` ...then I was going to be needing some custom `IEnumerator` implementation, because even if I could explicitly `.Cast()` every time, at runtime the cast would fail because the enumerator yields a COM object, not a wrapper type. So I made a generic `ComWrapperEnumerator`, where `TComCollection` is the collection type (must implement `IEnumerable`) and `TWrapperItem` is the type of the `SafeComWrapper` to be created and returned - am I doing this right? I don't like the way I'm assuming the constructor only has a single `T` parameter (where `T` is the COM type being wrapped by `TWrapperItem`), is there a better way? ``` using System; using System.Collections; using System.Collections.Generic; namespace Rubberduck.VBEditor.DisposableWrappers { public class ComWrapperEnumerator : IEnumerator where TComCollection : IEnumerable { private readonly IEnumerator _internal; public ComWrapperEnumerator(TComCollection items) { _internal = items.GetEnumerator(); } public void Dispose() { var disposable = _internal as IDisposable; if (disposable == null) { return; } disposable.Dispose(); } public bool MoveNext() { return _internal.MoveNext(); } public void Reset() { _internal.Reset(); } public TWrapperItem Current { get { return (TWrapperItem)Act
- pattern minor 112d agoWrapping COM objects with IDisposableOne of the things believed to contribute to destabilizing Rubberduck 2.x, is the fact that a lot of COM object references are stored in many places, and `Marshal.ReleaseComObject` is never called for those, which is what the alleged problem would be. To fix this, I've undertaken the task of completely wrapping the VBIDE API with managed types that implement `IDisposable` and call `Marshal.ReleaseComObject` when disposing. This presented several problems: - The wrapped object reference might be `null`; but wrapping a `null` reference means things like `if (wrapper == null)` wouldn't behave properly; hence, the wrappers should override `==` and `!=` operators to make null-checks work more intuitively. - The COM threading model (STA) isn't completely compatible with .NET's (MTA); although in theory all managed calls get marshaled into a STA call, in practice the managed RCW could be "disconnected" from the underlying COM object, which basically means literally anything can throw a `COMException` - including a simple getter read. Wrapping everything in `try/catch` blocks would make for excessively redundant code, hence a generic invocation mechanism was implemented, to invoke a COM object's member while catching a `COMException`. - Because the managed code implements `IDisposable`, we'll want an `ObjectDisposedException` to be thrown whenever a member is accessed on a disposed object. Hence, a general-purpose "throw if disposed" mechanism was implemented, to simplify the implementations. Here is the base class from which all wrappers will be derived: ``` using System; using System.Runtime.InteropServices; namespace Rubberduck.VBEditor.DisposableWrappers { public abstract class WrapperBase : IDisposable where T : class { private readonly T _item; private bool _isDisposed; protected WrapperBase(T item) { _item = item; } protected internal T Item { get {
- pattern minor 112d agoIDTExtensibility2 implementation for Rubberduck's entry pointHere is the new & improved Rubberduck 2.x entry point class, based on MZ-Tools 8.0's `Connect.VBA` implementation of the `IDTExtensibility2` COM interface, graciously shared to the Rubberduck team by Mr. MZ-Tools himself, Carlos Quintero. The modifications made are not yet merged into the project's main repository at the time of this writing, but you can view it on commit 8ac768d9 in my own fork. I've yet to shim the add-in to get it to be a good citizen and run in its own dedicated `AppDomain` (.net creates RCW's per-AppDomain, that's why it does matter), so there are still a number of issues beyond just the "entry point" to completely stabilize Rubberduck 2.x, but for now I'm interested in feedback specifically on the `IDTExtensibility2` implementation - a useful add-in, I'm discovering, is much trickier to "do right" than what the "hello world" tutorials imply. Also, since we're using ninject for dependency injection, I'm also interested in feedback on the composition root here, the handling of the `_kernel` instance and, given the various different ways an add-in can be loaded/unloaded, whether this (which seems to work fine) is done right. Of course, any other feedback is welcome, too. ``` using Extensibility; using Microsoft.Vbe.Interop; using Ninject; using Ninject.Extensions.Factory; using Rubberduck.Root; using Rubberduck.UI; using System; using System.ComponentModel; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; using System.Windows.Threading; using Ninject.Extensions.Interception; using NLog; using Rubberduck.Settings; using Rubberduck.SettingsProvider; namespace Rubberduck { /// /// Special thanks to Carlos Quintero (MZ-Tools) for providing the general structure here. /// [ComVisible(true)] [Guid(ClassId)] [ProgId(ProgId)] [EditorBrowsable(EditorBrowsableState.Never)] // ReSharper disable once InconsistentNaming // note: under
- pattern minor 112d agoShared VB6 *and* VBA extensibility add-in with OnConnect and OnDisconnect handlingRubberduck works well enough when the VBE is of the VBA variety, but it currently only works when the Rubberduck is configured to load at VBA start-up, and it has a tendency to crash the host application or fail to shutdown when exiting. There's a number of outstanding issues around being able to load Rubberduck after the IDE is already loaded, and to unload Rubberduck without closing the IDE. There's also a number of requests for adding the ability to work with VB5/VB6. While VB and VBA both have VBIDE libraries that are very similar, and they both support the `IDTExtensibility2` interface, they are not interchangeable. I've relied heavily on the MZ Tools documentation of Carlos Quintero of MZ-Tools, and the Microsoft documentation in coming up with a bare-bones solution that attempts to: - Allow for the add-in to be loaded/unloaded as the user requires. - Allow for the add-in to work under VB5/VB6 and 32/64-bit VBA. I've created my own interop assemblies for the respective VBEs: - VB6's VBIDE - `Fubaa.Interop.VB6Ext` - VBA's VBIDE - `Fubaa.Interop.VBA6Ext` Connect.cs ``` using Extensibility; using Fubaa.Interop.VB6Ext; using Fubaa.Interop.VBA6Ext; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace MyAddin { [ComVisible(true)] [Guid(ClassId)] [ProgId(ProgId)] public class Connect : IDTExtensibility2 { private const string ClassId = "506EC676-068F-497D-BEBB-B8EE42A34573"; private const string ProgId = "MyAddin.Connect"; private IVBE _vbe; private object _Addin; private bool isLoaded; public void OnAddInsUpdate(ref Array custom) { Debug.WriteLine("Begin Event: OnAddInsUpdate"); Debug.WriteLine(" Addin is Loaded:" + isLoaded.ToString()); Debug.WriteLine("End Event: OnAddInsUpdate"
- pattern minor 112d agoFind installed and available Windows UpdatesI have a function that uses Windows Update Agent (WUA) API through COM using the win32com.client module, part of pywin32 package for retrieving the installed and the available (not yet installed) Windows Updates. For each update, name, url of the relative Microsoft documentation and category is displayed. Finally the function returns 2 dictionaries containing a name and category of the installed updates and the available updates (not yet installed). This is the code I wrote, it compiles and produces the expected result: ``` import win32com.client import win32con import win32api import pywintypes import re def enum_winupdates(): wua = win32com.client.Dispatch("Microsoft.Update.Session") update_seeker = wua.CreateUpdateSearcher() # Search installed Software Windows Updates search_installed = update_seeker.Search("IsInstalled=1 and Type='Software'") updates_installed = win32com.client.Dispatch("Microsoft.Update.UpdateColl") print("\n[+] Enumerating installed Windows or Drivers' Updates...(if any)\n") installed_updates = [] installed_categories = [] available_updates = [] available_categories = [] installed_dict = {} available_dict = {} # compiles the regex pattern for finding Windows Update codes updates_pattern = re.compile(r'KB+\d+') for i in range(0, (len(search_installed.Updates))): # saves installed update name in a variable update_installed = search_installed.Updates.Item(i) for j in range(0, len(update_installed.Categories)): # extracts Windows Update code using regex update_code = updates_pattern.findall(str(update_installed)) # saves installed update category in a variable category = update_installed.Categories.Item(j).Name print("[*] Name: " + str(update_installed) + " - " + "url: " + "https://support.microsoft.com/en-us/kb/{}".format( "".join(update_code).strip("KB")) + " - " +
- pattern minor 112d agoA status bar for the VBEOne of the most annoying things about the VBE (VBA's IDE), is that it doesn't have a status bar. Rubberduck 2.0 works around this by introducing the `RubberduckCommandBar`, which [ab]uses `msoControlButton` controls as though they were labels - the command bar constantly resizes as the text changes, but at least we get to show context-sensitive information about the current selection: Because Rubberduck loads all referenced COM types and resolves references regardless of whether or not an identifier is built-in, the "status bar" also displays type information when the selection is on an identifier reference for a built-in declaration, e.g. an `Excel.ListObject`: ...or even built-in functions from the VBA standard library: If that's a wonderful feature, the code for it is a little less "wonderful". Here's the entire `RubberduckCommandBar` class, I'm particularly unimpressed with the `SetSelectionText` implementation - how can it be written better? ``` public class RubberduckCommandBar { private readonly RubberduckParserState _state; private readonly VBE _vbe; private readonly IShowParserErrorsCommand _command; private CommandBarButton _refreshButton; private CommandBarButton _statusButton; private CommandBarButton _selectionButton; public RubberduckCommandBar(RubberduckParserState state, VBE vbe, IShowParserErrorsCommand command) { _state = state; _vbe = vbe; _command = command; _state.StateChanged += State_StateChanged; Initialize(); } private void _statusButton_Click(CommandBarButton Ctrl, ref bool CancelDefault) { if (_state.Status == ParserState.Error) { _command.Execute(null); } } public void SetStatusText(string value = null) { UiDispatcher.Invoke(() => _statusButton.Caption = value ?? RubberduckUI.ResourceManager.GetString("ParserState_" + _state.Status)); } public void SetSelectionText(Declaration declar
- pattern minor 112d agoMilking a COM type library: "fun" with COM reflectionOnce upon a time, there was a duck that wanted to know where and how user code was calling into the VBA standard library and Excel object model. To match the rest of its API, the poor little duck had to dig through pages and pages and pages of MSDN documentation, and instantiate a `Declaration` object for each and every single module, class, enum, function, property, even and whatnot. For example, the `ColorConstants` built-in module from the VBA standard library would be hard-coded as a series of `Declaration` fields, and provided to rubberduck through a static getter that used reflection to pull all the members: ``` public static IEnumerable Declarations { get { if (_standardLibDeclarations == null) { var nestedTypes = typeof(VbaStandardLib).GetNestedTypes(BindingFlags.NonPublic).Where(t => Attribute.GetCustomAttribute(t, typeof(CompilerGeneratedAttribute)) == null); var fields = nestedTypes.SelectMany(t => t.GetFields()); var values = fields.Select(f => f.GetValue(null)); _standardLibDeclarations = values.Cast(); } return _standardLibDeclarations; } } //... private class ColorConstantsModule { private static readonly QualifiedModuleName ColorConstantsModuleName = new QualifiedModuleName("VBA", "ColorConstants"); public static readonly Declaration ColorConstants = new Declaration(new QualifiedMemberName(ColorConstantsModuleName, "ColorConstants"), VbaLib.Vba, "VBA", "ColorConstants", false, false, Accessibility.Global, DeclarationType.Module); public static Declaration VbBlack = new ValuedDeclaration(new QualifiedMemberName(ColorConstantsModuleName, "vbBlack"), ColorConstants, "VBA.ColorConstants", "Long", Accessibility.Global, DeclarationType.Constant, "0"); public static Declaration VbBlue = new ValuedDeclaration(new QualifiedMemberName(ColorConstantsModuleName, "vbBlue"), ColorConstants, "VBA.ColorConstants", "Long", Accessibility.Global, Declaration