I'm investigating Boo and thought it would be a useful exercise to try converting a couple of venerable VB Scripts that automate Excel (2007, in this instance). A lot of things seem to translate very easily, however I'm having a huge amount of trouble selecting ranges - whenever I try to get or set them I get a TargetInvocationException Member not found.
Here's a (cut down) example I've run in booish:
def CreateInstance(progid):
type = System.Type.GetTypeFromProgID(progid)
return type()
xl as duck = CreateInstance("Excel.Application")
xl.Visible = true
xl.Workbooks.Add
sht as duck = xl.ActiveSheet
#Next line throws exception
rng as duck = sht.Range("A1")
Certain things work fine, such as setting the Name property of the sheet and so on, but how do I work with ranges? Are there some special methods that VB hides that I'd need to call, and if so how would I go about finding those out?
Cheers,
Lenny.
Range is actually a property, and it's a somewhat special property in that it works as an Indexer, which means that it has array- or dictionary-like semantics. In most languages, that means that you'd access sht.Range["A1"]. That is syntactic sugar, and really it's accessed just like any other method, namely:
sht.get_Range("A1",System.Reflection.Missing.Method)
I attempted to use Boo, Ruby and IronRuby to repeat your code, using both the syntactic sugar style and the explicit method call. In IronRuby, I can get it to work flawlessly, but only in the 32-bit interpreter. In regular Ruby, which is a 32-bit app on my configuration, it also worked fine. In the 64-bit interpreter, the Range property was never resolved correctly.
So that led me to suspect that the Boo Interactive Shell was running in 64-bit mode and that interop was failing because of that. Unfortunately, the same issues reproduced after setting my local Boo binaries to run in 32-bit mode using CORFLAGS.exe, so I don't think that's the real issue.
What did work, though, was to explicitly import the Excel Dotnet Interop Library as well as the Interop services namespaces, like so:
import Microsoft.Office.Interop.Excel
import System.Runtime.InteropServices
xl_type=typeof(Application).GetCustomAttributes(typeof(CoClassAttribute),true)[0].CoClass
xl=xl_type()
xl.Visible=true
xl.Workbooks.Add
Then:
xl.Range["A1","A2"].Value=12
xl.Range["A1",System.Type.Missing].Value="Alpha"
(xl.ActiveSheet as Worksheet).Range["A1","A2"].Value2='Whatever'
All of these work, but they essentially require you to give up the "Scriptiness" you're used to from late-binding (which is what your duck typing is doing).
One difference from VB/VBScript that is true for most languages (other than C# 4.0) is that, generally, optional parameters are not handled transparently, so you'd need to look at the API more carefully when you deal with methods that support optional parameters (replacing them with System.Type.Missing or the System.Reflection equivalent). You'd find this through the Excel interop docs, although you can probably use reflection to identify parameters marked as optional if you find that easier than looking it up.
Because Ruby has a reasonable solution for late binding these objects, I suspect there's a missing feature (or bug) in COM interop scenarios in Boo.
Edited to add: Sam Ng writes about indexed property support in C# 4.0; the issues described in his post likely apply to Boo, as well.
Related
We build an OCX that contains a Map control and many supporting classes (e.g. GeoProjection, Extents) which can act as standalone classes and many of which are also Properties of the Map.
One of our legacy applications that uses the OCX is built in Visual Basic 6. After updating the build from VS2013 to VS2015, we noticed differences in how we have to access some of the properties from VB6. The two in particular that we had problems with were the Map.GeoProjection and the Map.Extents properties. They both started raising "Library Not Registered" errors when attempting to use them.
In the case of the GeoProjection, I was able to resolve the problem simply by adding the VB 'Set' command to the assignment, as follows:
Dim gp As New GeoProjection
gp.ImportFromEPSG m_MapProjection ' contains the EPSG code
Set Map.GeoProjection = gp.Clone
Why the 'Set' wasn't required before this, I don't know. It seems like it should have been since it is setting an object reference.
Even so, when I tried to do the same with the Extents property, it did not resolve the problem, but instead resulted in an "Automation Error". So I next went to review the IDL and MFC macro definitions to see how the definitions differed and why the behavior might differ.
The GeoProjection IDL
[id(192)] IGeoProjection* GeoProjection;
end the Extents IDL
[id(17), propput, nonbrowsable] void Extents(IExtents* nNewValue);
[id(17), propget, nonbrowsable] IExtents* Extents();
The GeoProjection dispatch macro
DISP_PROPERTY_EX_ID(CMapView, "Projection", dispidProjection, GetGeoProjection, SetGeoProjection, VT_DISPATCH)
and the Extents dispatch macro
DISP_PROPERTY_EX(CMapView, "Extents", GetExtents, SetExtents, VT_DISPATCH)
What particularly struck me was the difference in using the DISP_PROPERTY_EX vs the DISP_PROPERTY_EX_ID. Ironically, I can find no Microsoft Documentation on DISP_PROPERTY_EX_ID. The only guidance I could find were comments in the afxdisp.h file, where it describes the two groups of macros, as follows:
on line 288, prior to the macros without the _ID suffix
// these DISP_ macros cause the framework to generate the DISPID
and on line 313, prior to the _ID suffixed macros
// these DISP_ macros allow the app to determine the DISPID
Since we have an .h file that defines dispatch IDs for every function, it seems that we should always use those IDs within the macros, as follows. This assures that the Dispatch IDs will always be the same (which I think would matter when using early-bound COM calls).
DISP_PROPERTY_EX_ID(CMapView, "Extents", dispidExtents, GetExtents, SetExtents, VT_DISPATCH)
Once I made changes to the Extents property in the IDL
[id(17)] IExtents* Extents;
and in the use of the DISP_PROPERTY_EX_ID macro, we were able to successfully set the Extents property from VB6 using the 'Set' statement.
In summary, I don't really know
why the behavior changed in the first place (from VS2013 to VS2015)?
why my changes made things start working again (I have my theories)?
what is the recommended methodology going forward?
a. IDL syntax allows for property definition without having to specify propput and propget. Is one syntax recommended over the other?
b. should I change all of our macros to use the _ID suffixed macro?
Any insight is appreciated.
Thank you.
I'm currently going through my project in Jetbrains Pycharm 2017.1.5, documenting all my python 3.6 classes and methods, and several things stand out to me about the docstring format.
I want to link to other methods / functions / classes from some of the docstrings, but I cannot figure out how to do this. The documentation for restructuredText is very, very extensive, but it doesn't say anything about referencing other docstrings with Pycharm. In fact, the vast majority of the snippets from that page do not even work in Pycharm. (Why is that?)
I've managed to find that you can use :class:`<class_name>` to reference a class, but :class:`<class.method>`does not work, and similarly named constructs like :func:`<func_name>` do not create a hyperlink. I've also seen :ref:`<name>` come up, but that one doesn't work either.
(I would switch to Epytext (it has everything I want, plus it's much simpler) in a heartbeat if not for this error: You need configured Python 2 SDK to render Epydoc docstrings in the Ctrl + Q frame.)
It would also be extremely helpful if there was a way to inherit the docstring in subclasses / overridden methods. Pycharm does this automatically if you leave the docstring blank, which makes me think it is possible to do it manually. But, again, I can't find any information on it.
It's turning out to be mind-blowingly complicated to do something so, so simple. So, any help will be appreciated!
I want to link to other methods / functions / classes from some of the docstrings, but I cannot figure out how to do this.
You're correct that the reStructuredText documentation does not cover this, because it's not a feature of reStructuredText.
Likely you are (explicitly, or implicitly via some tool) using the Sphinx system – a superset of Docutils – to allow (among many other features) references between different docstrings.
Sphinx defines several Docstring “roles” (the :foo: before the backtick-quoted text) for different purposes:
doc, a reference to an entire document.
ref, an arbitrary cross-reference.
… many others.
For specifically Python code, the “domain” py has its own specific set of roles for Python code docstrings:
:py:mod:
Reference a module; a dotted name may be used. This should also be used for package names.
:py:func:
Reference a Python function; dotted names may be used. The role text needs not include trailing parentheses to enhance readability; they will be added automatically by Sphinx if the add_function_parentheses config value is True (the default).
:py:data:
Reference a module-level variable.
:py:const:
Reference a “defined” constant. This may be a Python variable that is not intended to be changed.
:py:class:
Reference a class; a dotted name may be used.
:py:meth:
Reference a method of an object. The role text can include the type name and the method name; if it occurs within the description of a type, the type name can be omitted. A dotted name may be used.
:py:attr:
Reference a data attribute of an object.
:py:exc:
Reference an exception. A dotted name may be used.
:py:obj:
Reference an object of unspecified type.
So I'm working with my program. It works and runs, so I copied it and made a second version which I can add extra features to while still having a working program. I don't see any problems with my new code, so I prepare to step thru it. Suddenly things which work perfectly fine in my other version are throwing compile errors. Specifics below.
Dim elapsedTime As String
Dim startTime As Date
startTime = Now()
'code
elapsedTime = Format(Now() - startTime, "h:m:s")
I checked again and again, but this exact same code works fine in my other file. In this file, it highlights "Format" and throws the following Compile Error.
Does anyone know why excel would be getting so upset at this? If there are compile errors, should I even be looking at the places it highlights at all?
You have something named Format somewhere that's in-scope (i.e. accessible from where that Format function is being invoked), and that Format thing is shadowing the Format function you mean to call.
As YowE3K suggested if you right-click on that Format call and select "Definition" (or Shift+F2), that will take you to the shadowing declaration.
From there you have several options:
Rename the Format identifier [and possibly break a ton of things in your code]
Fully-qualify that Format call, i.e. VBA.Strings.Format, or just Strings.Format, or even VBA.Format.
My suggestion would be to fully-qualify that call, then try to compile again, and fully-qualify any/all calls that cause that same compile error. Then rename whatever it is that is named Format, so that it no longer shadows the function from the VBA standard library.
If you're using Rubberduck (an open-source VBIDE add-in project I manage), you can easily locate all references of the shadowing identifier:
The Rubberduck toolbar tells you when Format is referring to the correct function:
And you can easily locate all references to that as well, qualified or not - so it's easy to fully-qualify them wherever they are.
Rubberduck also makes it easy to refactor/rename the function, in a way that search/replace fails:
Coincidentally, a new code inspection is currently in works, specifically to locate shadowing identifiers like this.
As for the ByRef argument type mismatch, it seems your screenshot doesn't match with the code you posted, so anything is possible, but unless eqn is assigned in the body of that MakeEqn function (it should be returned, not assigned), then there's no need to pass it ByRef (implicitly or explicitly) - it can be passed ByVal (Rubberduck gives you an inspection result for that - and another for the implicit return type, and another for the implicit Public access modifier on both procedures, and several others for other issues in the code you posted).
I've been tasked with creating conformance tests of user input, the task if fairly tricky and we need very high levels of reliability. The server runs on PHP, the client runs on JS, and I thought Haxe might reduce duplicative work.
However, I'm having trouble with deadcode removal. Since I am just creating helper functions (utilObject.isMeaningOfLife(42)) I don't have a main program that calls each one. I tried adding #:keep: to a utility class, but it was cut out anyway.
I tried to specify that utility class through the -main switch, but I had to add a dummy main() method and this doesn't scale beyond that single class.
You can force the inclusion of all the files defined in a given package and its sub packages to be included in the build using a compiler argument.
haxe --macro include('my.package') ..etc
This is a shortcut to the macro.Compiler.include function.
As you can see the signature of this function allows you to do it recursive and also exclude packages.
static include (pack:String, rec:Bool = true, ?ignore:Array<String>, ?classPaths:Array<String>):Void
I think you don't have to use #:keep in that case for each library class.
I'm not sure if this is what you are looking for, I hope it helps.
Otherwise this could be helpful checks:
Is it bad that the code is cut away if you don't use it?
It could also be the case some code is inlined in the final output?
Compile your code using the compiler flag -dce std as mentioned in comments.
If you use the static analyzer, don't use it.
Add #:keep and reference the class+function somewhere.
Otherwise provide minimal setup if you can reproduce.
With Java on one side and Ruby/Groovy on the other, I know that in the second camp I'm free to make typos which will not get caught until run-time. Is this true of all dynamically-typed languages?
Edit: I've been asked to elaborate on the type of typo. In Ruby and in Groovy, you can assign to a variable with an accidental name that is never read. You can call methods that don't exist (obviously your tests should catch this, it's been said). You can refer to classes that don't exist, etc. etc. Basically any valid syntax, even with typographical errors, is valid in both Ruby and Groovy.
In Perl, if you declare use strict in your code, then you must declare your variables with my. Typos in variable names will then be caught at compile-time. This is one of the biggest things I miss when coding in Python.
Python is typo-friendly in the way you described in your question.
But this does not mean that these 'typos' can only be caught # runtime.
When using a code analyzer like pylint (ideally integrated into your development environment) you'll catch 'most' of these consistently before hitting 'run'.
For the most part, yes. Dynamic typing and not requiring declaration of variables are language properties that are frequently found together.
However, these are not inherently related. A language can easily have dynamic typing while requiring variable names to be declared before use. As ire_and_curses mentions, this can be achieved in Perl via the "use strict" directive.
Here's what happens when I try to get into the pitfalls you mentioned in Squeak and Dolphin, two implementations of the dynamic language Smalltalk 80.
You can assign to a variable with an accidental name that is never read
The Smalltalk language requires temp and instance variables to be declared. If I try to compile a method containing an undefined variable I get a compile-time error.
| anArray |
anArrray := Array with: 2 with: 1. "Unknown variable anArrray"
Creating variables dynamically is not something dynamic languages have to allow. There's a difference between typeless declarations and no declaration at all.
You can call methods that don't exist
The compiler issue a warning if you use a selector (i.e. method name) that is entirely unknown.
The compiler won't bother if I call the method paint on an array because there's another class in the system implementing paint. That error will only be caught at runtime.
If however I call the method sortt (while I intend to call sort) the compiler generates a warning. When developing top-down you can proceed pass these warnings.
| anArray |
anArray := Array with: 2 with: 1.
anArray paint. "Runtime error. You can't paint an array but perhaps a Shape"
anArray sortt. "Compile-time warning"
You can refer to classes that don't
exist
This is not allowed. Though in Squeak you can quickly create a new class from the error dialog if needed.