C++/CLI Managed Wrapper and ADODB::Recordset - visual-c++

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));

Related

referencing ActiveX in F#

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.

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.

InstallShield calling advapi32.dll method type mismatch error

I am trying to call Advapi32.LsaOpenPolicy() from basic MSI InstallShield code. I've successfully called other avdapi32.dll methods; But LsaOPenPolicy is throwing a mismatched type error.
My prototype is:
prototype INT Advapi32.LsaOpenPolicy(POINTER, POINTER, INT, POINTER);
The windows definition is:
NTSTATUS LsaOpenPolicy(
_In_ PLSA_UNICODE_STRING SystemName,
_In_ PLSA_OBJECT_ATTRIBUTES ObjectAttributes,
_In_ ACCESS_MASK DesiredAccess,
_Inout_ PLSA_HANDLE PolicyHandle
);
I've noted in C++ samples that the ObjectAttriibute structure is zeroed out. So I do something similar here in the InstallShield code -- pArray points to the array contents.
for i = 0 to 11
array(i) = 0;
endfor;
array(0) = 24;
// current error is 80020005 type mismatch.
try
i = POLICY_CREATE_ACCOUNT | POLICY_LOOKUP_NAMES;
pArray = array;
pPolicy = NULL;
nvOSResult = LsaOpenPolicy(NULL, pArray, i, pPolicy);
catch
Sprintf(errString, "0x%08x", Err.Number);
_Logger(hMSI, methodName, "LsaOpenPolicy Exception "+errString, INFORMATION, FALSE);
nvOSResult = Err.Number;
endcatch;
There not much other information I can find other than the 80020005 error thrown; I've tried a few different argument constructions, but I can't get past this.
I've posted this in an flexera and microsoft forum -- but I have gotten no traction there. (references for posterity: flexera-link, microsoft-link)
Any help or suggestions are welcome!
The answer to this question was to actually work-around the interface between installshield and the system DLLs by moving all the workings into a C++ DLL. As installation got more complex, I ended up with two separate DLL functions, one executed at dialog (non-admin) mode and one at deferred execution (admin) mode.
In order to pass information I used the MsiGetProperty() API using MSI properties for both input and output variables.
Note that for deferred execution, I needed a CAD function on the installshield side to marshal data into the custom action data location, and on the DLL side extract the data, again by using MsiGetProperty() but getting the "CustomActionData" property and then parse the resulting string which contained the marshaled data.

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()

Resources