referencing ActiveX in F# - reference

If I want to use ActiveX in .NET project, I add it as reference using Add Reference... dialog box and it generates interop assembly, which actually is referenced.
But if I want to use ActiveX from F# Interactive, should I first create F# project, add ActiveX reference and then reference generated interop assembly from F#:
#r "obj\\Interop.MyActiveX.dll"
or it's possible to reference it directly, for example as in VB Script:
Set mydoc = CreateObject("MyActiveX")
or in PowerShell:
$mydoc = new-object -comobject MyActiveX

You can create an ActiveX control directly like this:
let actxtype = Type.GetTypeFromProgID("MyActiveX")
let instance = Activator.CreateInstance(actxtype)
but your instance will be of type 'obj = System.__ComObject' and you will have to call its methods using reflection something like this:
actxtype.InvokeMember("MethodName", BindingFlags.InvokeMethod | BindingFlags.Public,
null, instance, new[] { (*method parameters*) } );
Here is doc on Invoke: http://msdn.microsoft.com/en-us/library/de3dhzwy(v=vs.110).aspx
If you reference an interop assembly you'll have 'normal' type with properties and methods.

Related

How to select value from a dropdown using JScript (not JavaScript) with TestComplete

Currently I'm working on TestComplete automation tool. I'm facing a problem in selecting a value from a dropdown using Jscript. It can done easily in javascript by
document.getElementById("id").options[1].selected=true
I cant do it using JScript'. I've tried
Browsers.Item(btIExplorer).Run("Page URL"); //opening the browser and running a URL
browser=Aliases.browser; //creating alias of browser object
page=browser.Page("PageURL"); //creating a page object for the page opened
page.NativeWebObject.Find("id", "defaultLocationBinder_newOrExisting", "input") // This is the dropdown
I'm not able to find any suitable option to select the options in the dropdown which are given in the <option></option> tags
I just wrote this piece of code and was able to do it.
Use the selectedIndex to set the option you want.
use the object spy to check the properties/methods you can use with the object.
function loginDropDown()
{
var dropDown = Sys.Browser("iexplore").Page("*").FindChild("Name","Select(\"myList\")",10,true)
dropDown.selectedIndex = 1
}
The NativeWebObject.Find method returns a native object while you may want to work with a TestComplete wrapper. Use the Find or FindChild method to get such a wrapper and the Clickitem method to select a specific item.
function test()
{
var b = Sys.Browser("iexplore");
b.ToUrl("http://support.smartbear.com/message/?prod=TestComplete");
var page = b.Page("http://support.smartbear.com/message/?prod=TestComplete");
var cBox = page.FindChild("ObjectIdentifier", "ddlRequestType", 20);
cBox.ClickItem("General product question");
}

TestComplete Objects - Enumerate properties

For the TestComplete objects of processes, windows and controls on the screen - is there a way to enumerate and print out all the properties. I tried the following code and I get a runtime exception:-
var deskObj = Sys.Desktop; //TC Desktop Object
var normObj = {a:1, b:2, c:3}; //Normal JScript Object
for (var prop in normObj)
{
Log.Message(normObj[prop]); //1, 2, 3
}
for (var prop in deskObj) //Runtime error - Object doesn't support this action
{
Log.Message(deskObj[prop]);
}
This leads me to believe that TC Objects are not quite JScript objects - so is there a way to convert these to JScript Objects.
That's right: objects from the Sys tree (the object tree in the Object Browser) are special COM wrappers for actual application objects. They are not common JScript objects.
To get the list of properties and methods of such TestComplete objects, you can use the GetProperties and GetMethods methods of the aqObject object. You can find sample code within the corresponding help topics.

Using late binding to Excel, how do I move from the active cell to the first cell in the next row?

I am using late binding (in C#, using Reflection, etc.) to access Excel via COM. In other words, I start by getting the Excel.Application object using
Type excelType = Type.GetTypeFromProgId("Excel.Application");
object excelApplication = Activator.CreateInstance(excelType);
(Actually, it's more generic than that, but that gives the idea).
As many people have noted in StackOverflow, one good way to see how to use the COM interfaces to Office is to record a macro in Excel and then look at the VBA code to see which class members to invoke.
When I do that, I see that if you have a reference to the active cell (obtained from the ActiveCell property of the Excel.Application object), then VBA references ActiveCell.Offset(x,y) to reference a cell relative to that ActiveCell (x and y being the row and column indices). However, if I try to access the Offset method of a Cell with late binding, the InvokeMember method fails with an exception saying "Member not found".
If I use the Visual Studio Object Browser to inspect the Microsoft.Office.Interop.Excel namespace and the ApplicationClass class (which I assume gives a good look into the COM interface obtained by the above C# code getting it via the ProgId), it shows that ApplicationClass has an ActiveCell property which is of type Microsoft.Office.Interop.Excel.Range. And inspecting that class shows that indeed it does NOT have a member named "Offset", hence the "Member not found" when I try to Invoke it.
That seems to mean that the VBA macros are using a different object model for Excel from that which is exposed via COM! Is that right, or am I missing something? And if it is the case, how can I get at that same VBA object model via late binding in COM?
Or, is there some way, using methods other than Offset, to be able to move the ActiveCell to the beginning of the next row, which is really what I want?
You could try this:
dim i as integer
i = ActiveCell.Row + 1
Excel.ActiveSheet.Cells(i,1).value = "Whatever your value is."
The answer is, as usual, easy when you know how.
My code a bit further down from the sample in my question looked a bit like this:
object activeCell;
object result;
// in here code to get the correct value into activeCell
if(activeCell.GetType().GetProperty("Offset") == null){
result = activeCell.GetType().InvokeMember("Offset", BindingFlags.InvokeMethod, null, activeCell, new object[] {1, 0});
}
It was that InvokeMember that threw an exception with the message "Member not found".
When I changed the parameters to InvokeMember to read as follows:
result = activeCell.GetType().InvokeMember("Offset", BindingFlags.InvokeMethod | BindingFlags.GetProperty, null, activeCell, new object[] {1, 0});
then it worked.
Just why it works is still a mystery, which I am sure will earn someone some reputation if they answer it.

XXX doesn't contain a definition and no extension method

I need to access some methods and properties of a third party unmanaged DLL from my VS2010 C# project. One property in particular “disappears” when trying to access it after I added the DLL to the reference. I am using MS VS2010 and the target platform is an XP SP3 x86.
From the .NET VB, the Item property is shown as
Item([Object], [Object]) As Object
or
ReadOnly Default Property Item(Optional ByVal Name As Object = Nothing, Optional ByVal Index As Object = Nothing) As Object
I can use it with no problem.
However, in C#, this property disappears and the closest one I can find become
this[[object], [object]]
or
dynamic this[[object Name = System.Type.Missing], [object Index = System.Type.Missing]] { get; }
How do I access this property in my C# project? Thanks.
The Item property in VB.NET is the indexer in C#.
So, the following VB.NET and C# codes are equivalent:
/* VB.NET */
yourObject.Item(o1, o2)
/* C# */
yourObject[o1, o2];
this is an indexer and can be accessed like this.
var yourObj = new SomeObject();
var item = yourObj[value1,value2];
In other words you just use [] brackets after the object variable itself, rather than Item()

C++/CLI Managed Wrapper and ADODB::Recordset

I have a native C++ DLL that uses COM ADO Recordsets and am in need of converting it to the .NET variant (ADODB::Recordset). I have tried several approaches to tackling this problem without success.
The native C++ DLL dynamically creates and populates the COM Recordset. Ideally I'd do the same for the ADODB::Recordset within the managed wrapper, but the needed properties aren't accessible to me.
For example, when attempting to utilize the Fields collection in order to Append Columns (despite intellisense telling me otherwise), I receive:
error C2039: 'Fields' : is not a member of 'ADODB::Recordset'
ADODB::Recordset ^RS = gcnew ADODB::Recordset ();
RS->Fields->Append("ID", DataTypeEnum::adInteger, 1, FieldAttributeEnum::adFldKeyColumn);
My C++/CLI solution contains the ADODB reference (c:\Program Files\Microsoft.NET\Primary Interop Assemblies\adodb.dll) version 7.0.3300.0
I am using Visual Studio 2005 with .NET Framework 2.0.50727 SP2
I would appreaciate it if someone in the StackOverflow community can direct me to a sample that dynamically populates a .NET ADO Recordset using C++/CLI.
I think that the main difference between the raw COM Recordset and the one provided by the .Net wrapper classes is just that some things get renamed. It is simply a wrapper around the underlying COM object and not a new class in its own right.
To answer your immediate example, try
RS->default->Append(...);
You could try to run ILDASM over the adodb.dll you have to see what the API on the recordset class is.
You can do the following too:
//Create instance of a recordset
ADODB::RecordsetClass^ recordset;
recordset = gcnew ADODB::RecordsetClass();
//Set some options
recordset->CursorLocation = ADODB::CursorLocationEnum::adUseClient ;
recordset->CursorType = ADODB::CursorTypeEnum::adOpenDynamic;
recordset->LockType = ADODB::LockTypeEnum::adLockBatchOptimistic;
//Add columns
recordset->default->Append("Name", ADODB::DataTypeEnum::adWChar, 50, ADODB::FieldAttributeEnum::adFldFixed, nullptr);
recordset->default->Append("Number", ADODB::DataTypeEnum::adWChar, 20, ADODB::FieldAttributeEnum::adFldFixed, nullptr);
//Build an array of field names
fields = gcnew array<Object^>(2);
fields[0] = gcnew String("Name");
fields[1] = gcnew String("Number");
//Add values
array<Object^>^ values = gcnew array<Object^>(2);
values [0] = "some name";
values [1] = 1.2;
recordset->AddNew(fields, values);
//Get a value out again
recordset->MoveFirst();
ADODB::Field^ pNum= recordset->default[1];
double num = Convert::ToDouble((pNum->default));

Resources