I'm having a strange issue while using the MLApp.GetWorkspaceData function. I noticed that this functions works properly when I do the following:
matlab = new MLApp.MLAppClass();
object myObject;
matlab.GetWorkspaceData("myVariable", "base", out myObject);
But if I then try to use the same object as an output I get an "Invalid Callee" exception. In addition this also gives the same error:
matlab = new MLApp.MLAppClass();
object myObject = new object();
matlab.GetWorkspaceData("myVariable", "base", out myObject);
This is very troublesome because I need to get a large amount of data from Matlab to Visual Studio, and I cannot practically create 52K uninitialized variables and keep them around. Is there some way to "uninitialize" a variable? Is there some concept I'm missing here?
As #wonko79 explained in the comments, if you want to reuse the out variable, you should set it to null first.
Here is a tested example calling MATLAB from C#:
using System;
namespace CSharp_matlab_com
{
class Program
{
static void Main(string[] args)
{
MLApp.MLAppClass matlab = new MLApp.MLAppClass();
// create variables: a_0, a_1, ..., a_4
for (int k = 0; k < 5; k++) {
matlab.Execute(string.Format("a_{0} = rand(2);", k));
}
// retrieve variables from MATLAB and print their contents
object a;
for (int k = 0; k < 5; k++) {
// current variable name
string varname = string.Format("a_{0}", k);
// get data array
a = null; // without this line, an exception is thrown!
matlab.GetWorkspaceData(varname, "base", out a);
// print contents
var arr = (double[,]) a;
Console.WriteLine("\nndims(a) = {0}, numel(a) = {1}", arr.Rank, arr.Length);
for (int i = 0; i < arr.GetLength(0); i++) {
for (int j = 0; j < arr.GetLength(1); j++) {
Console.WriteLine("{0}[{1},{2}] = {3}", varname, i, j, arr[i,j]);
}
}
}
}
}
}
The output:
ndims(a) = 2, numel(a) = 4
a_0[0,0] = 0.251806122472313
a_0[0,1] = 0.617090884393223
a_0[1,0] = 0.290440664276979
a_0[1,1] = 0.265280909810029
...
ndims(a) = 2, numel(a) = 4
a_4[0,0] = 0.425259320214135
a_4[0,1] = 0.16148474431175
a_4[1,0] = 0.312718886820616
a_4[1,1] = 0.178766186752368
You can create a wrapper for GetWorkspaceData method, like in the next example:
public object GetData(string name)
{
object data;
mlApp.GetWorkspaceData(name, "base", out data);
return data;
}
Or, even more useful, a generic wrapper:
public T GetData<T>(string name)
{
object data;
mlApp.GetWorkspaceData(name, "base", out data);
if (data == null)
return default(T);
if (data is T)
return (T)data;
else
throw new InvalidCastException($"The variable '{name}', of type '{data.GetType().Name}' cannot be casted to type '{typeof(T).Name}'.");
}
The solution is to set the output object to null.
I found it here.
Related
Why am I getting byte 0x00 at index 0 instead of 0xD0 when calling the SetData() method from outside the class?
Here's a shortened down of the class:
using System;
namespace StackOverflow
{
class Section
{
uint _sectionId;
byte[] _sectionData;
public Section(byte[] sectionData) {
_sectionData = sectionData;
_sectionId = GetUShort(0x0FF4);
Console.WriteLine(_sectionId + ": " + _sectionData[0].ToString("X")); // Gives D0
}
public void SetData() {
switch (_sectionId) {
case 0: SetTrainerInfo(); break;
default: break;
}
}
void SetTrainerInfo() {
Console.WriteLine(_sectionId + ": " + _sectionData[0].ToString("X")); // Can Give 0
byte[] deByte = GetBytes(0x00, 7);
TrainerInfo.playerName = PkmString.GetString(deByte);
Console.WriteLine(TrainerInfo.playerName + "name here");
}
}
}
Works fine if called in the Section constructor, but not from an external source like this:
using System;
namespace StackOverflow
{
class SaveBlock
{
const short SECTIONS = 14, SECTION_SIZE = 4096;
public Section[] sections = new Section[SECTIONS];
public SaveBlock(byte[] blockData) {
byte[] sectionData = new byte[SECTION_SIZE];
for (int i = 0; i < SECTIONS; i++) {
Array.Copy(blockData, SECTION_SIZE * i, sectionData, 0, SECTION_SIZE);
sections[i] = new Section(sectionData);
}
}
public void SetForAll() {
foreach (Section sect in sections)
sect.SetData();
}
}
}
And I get different bytes for the same element:
Look at this code from the SaveBlock constructor:
byte[] sectionData = new byte[SECTION_SIZE];
for (int i = 0; i < SECTIONS; i++) {
Array.Copy(blockData, SECTION_SIZE * i, sectionData, 0, SECTION_SIZE);
sections[i] = new Section(sectionData);
}
The loop copies the blockData bytes to the sectionData array, and then uses sectionData to create a new Section object instance. Here's where things get tricky... arrays are reference types. That means the following line in your Section constructor is only copying the reference to the array:
_sectionData = sectionData;
As you move through the loop, you're copying data into the same array instance over and over, and then assigning that same array instance to the internal data of several Section objects. All of those objects are sharing the same array instance! It's very likely you want a new array for each of those objects:
byte[] sectionData;
for (int i = 0; i < SECTIONS; i++) {
sectionData = new byte[SECTION_SIZE];
Array.Copy(blockData, SECTION_SIZE * i, sectionData, 0, SECTION_SIZE);
sections[i] = new Section(sectionData);
}
That may not be the only problem, as _sectionId is shown defined private an initialized to non-0. The SetData() method looks like this:
public void SetData() {
switch (_sectionId) {
case 0: SetTrainerInfo(); break;
default: break;
}
}
From the code we can see, there is no way that _sectionId will ever be 0, and thus no way that SetTrainerInfo() is ever called.
I want to write extension methods for converting a vector and matrix into string. I did this in the following way.
For Vector
public static string GetString<T>(this T[] SourceMatrix, string ColumnDelimiter = " ")
{
try
{
string result = "";
for (int i = 0; i < SourceMatrix.GetLength(0); i++)
result += SourceMatrix[i] + ColumnDelimiter;
return result;
}
catch (Exception ee) { return null; }
}
For Matrix
public static string GetString<T>(this T[][] SourceMatrix, string ColumnDelimiter = " ", string RowDelimiter = "\n")
{
try
{
string result = "";
for (int i = 0; i < SourceMatrix.GetLength(0); i++)
{
for (int j = 0; j < SourceMatrix[i].GetLength(0); j++)
result += SourceMatrix[i][j] + "" + ColumnDelimiter;
result += "" + RowDelimiter;
}
return result;
}
catch (Exception ee) { return null; }
}
Now i am using following code which causes ambiguity.
List<double[]> Patterns= GetPatterns();
Patterns.ToArray().GetString();
Error
Error 5 The call is ambiguous between the following methods or properties:
'MatrixMathLib.MatrixMath.GetString<double[]>(double[][], string)' and
'MatrixMathLib.MatrixMath.GetString<double>(double[][], string, string)'
Can anyone suggest me to write these extension methods correctly.
Thanks in advance.
There is nothing wrong with your methods. It is the compiler that can't choose between them.
As you can see in the error message, the compiler could assume T to be double[] and match the first method, or double and match the second one. This will be solved by explicitly mentioning which method you want to use:
Patterns.ToArray().GetString<double>();
You can either omit the default values or state the type of T in the function call
The compiler can't tell whether you want to call GetString<double[]> or GetString<double> because both methods fit the invocation.
The easiest way to solve that is to simply change one of the names (i.e. GetArrayString<T>). A better solution IMO is to have only 1 method that solves both cases like this one:
public static string Join<T>(this T[] sourceMatrix, string columnDelimiter = " ", string rowDelimiter = "\n")
{
if (sourceMatrix.Length == 0)
{
return string.Empty;
}
if (sourceMatrix[0] as Array == null)
{
return string.Join(columnDelimiter, sourceMatrix);
}
var rows = new List<string>();
foreach (T item in sourceMatrix)
{
var array = item as Array;
var row = "";
for (int j = 0; j < array.GetLength(0); j++)
row += array.GetValue(j) + columnDelimiter;
rows.Add(row);
}
return string.Join(rowDelimiter, rows);
}
Usage:
int[] a = {1, 2, 3};
int[] b = { 1, 2, 4 };
int[][] c = {a, b};
Console.WriteLine(a.Join());
Console.WriteLine();
Console.WriteLine(c.Join());
Output:
1 2 3
1 2 3
1 2 4
Note: This only solves for 1-2 dimensions, but can easily be generalized to n dimensions.
I am trying to return dynamic array with help of Excel-DNA api. I am able to produce desired output. But I want to produce output in vertical .
here is the c# code for my UDF. i have used this link for producing my sample. http://excel-dna.net/2011/01/30/resizing-excel-udf-result-arrays/
Below code produces this output:
(0,0) (0,1) (0,2) (0,3)
I want output in this format:
(0,0)
(0,1)
(0,2)
(0,3)
Here is the code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using CometCollaborator;
using ExcelDna.Integration;
namespace ExcelUDFs
{
public class UDFs
{
[ExcelFunction(Description = "Make Array")]
public static object MakeArrayAndResize(int columns)
{
object result = MakeArray(columns);
// Call Resize via Excel - so if the Resize add-in is not part of this code, it should still work.
var a = XlCall.Excel(XlCall.xlUDF, "Resize", result);
return a;
}
[ExcelFunction(Description = "Make Array")]
public static object MakeArray( int columns)
{
object[,] result = new string[1, columns];
for (int i = 0; i < 1; i++)
{
for (int j = 0; j < columns; j++)
{
result[i, j] = string.Format("({0},{1})", i, j);
}
}
return result;
}
static Queue<ExcelReference> ResizeJobs = new Queue<ExcelReference>();
// This function will run in the UDF context.
// Needs extra protection to allow multithreaded use.
public static object Resize(object[,] array)
{
ExcelReference caller = XlCall.Excel(XlCall.xlfCaller) as ExcelReference;
if (caller == null)
return array;
int rows = array.GetLength(0);
int columns = array.GetLength(1);
if ((caller.RowLast - caller.RowFirst + 1 != rows) ||
(caller.ColumnLast - caller.ColumnFirst + 1 != columns))
{
// Size problem: enqueue job, call async update and return #N/A
// TODO: Add guard for ever-changing result?
EnqueueResize(caller, rows, columns);
AsyncRunMacro("DoResizing");
return ExcelError.ExcelErrorNA;
}
// Size is already OK - just return result
return array;
//object[,] retArray = new object[columns, rows];
//for(int i=0;i<array.Length;i++)
//{
// retArray[i, 0] = array[0, i];
//}
//return retArray;
}
static void EnqueueResize(ExcelReference caller, int rows, int columns)
{
ExcelReference target = new ExcelReference(caller.RowFirst, caller.RowFirst + rows - 1, caller.ColumnFirst, caller.ColumnFirst + columns - 1, caller.SheetId);
ResizeJobs.Enqueue(target);
}
public static void DoResizing()
{
while (ResizeJobs.Count > 0)
{
DoResize(ResizeJobs.Dequeue());
}
}
static void DoResize(ExcelReference target)
{
try
{
// Get the current state for reset later
XlCall.Excel(XlCall.xlcEcho, false);
// Get the formula in the first cell of the target
string formula = (string)XlCall.Excel(XlCall.xlfGetCell, 41, target);
ExcelReference firstCell = new ExcelReference(target.RowFirst, target.RowFirst, target.ColumnFirst, target.ColumnFirst, target.SheetId);
bool isFormulaArray = (bool)XlCall.Excel(XlCall.xlfGetCell, 49, target);
if (isFormulaArray)
{
object oldSelectionOnActiveSheet = XlCall.Excel(XlCall.xlfSelection);
object oldActiveCell = XlCall.Excel(XlCall.xlfActiveCell);
// Remember old selection and select the first cell of the target
string firstCellSheet = (string)XlCall.Excel(XlCall.xlSheetNm, firstCell);
XlCall.Excel(XlCall.xlcWorkbookSelect, new object[] { firstCellSheet });
object oldSelectionOnArraySheet = XlCall.Excel(XlCall.xlfSelection);
XlCall.Excel(XlCall.xlcFormulaGoto, firstCell);
// Extend the selection to the whole array and clear
XlCall.Excel(XlCall.xlcSelectSpecial, 6);
ExcelReference oldArray = (ExcelReference)XlCall.Excel(XlCall.xlfSelection);
oldArray.SetValue(ExcelEmpty.Value);
XlCall.Excel(XlCall.xlcSelect, oldSelectionOnArraySheet);
XlCall.Excel(XlCall.xlcFormulaGoto, oldSelectionOnActiveSheet);
}
// Get the formula and convert to R1C1 mode
bool isR1C1Mode = (bool)XlCall.Excel(XlCall.xlfGetWorkspace, 4);
string formulaR1C1 = formula;
if (!isR1C1Mode)
{
// Set the formula into the whole target
formulaR1C1 = (string)XlCall.Excel(XlCall.xlfFormulaConvert, formula, true, false, ExcelMissing.Value, firstCell);
}
// Must be R1C1-style references
object ignoredResult;
XlCall.XlReturn retval = XlCall.TryExcel(XlCall.xlcFormulaArray, out ignoredResult, formulaR1C1, target);
if (retval != XlCall.XlReturn.XlReturnSuccess)
{
// TODO: Consider what to do now!?
// Might have failed due to array in the way.
firstCell.SetValue("'" + formula);
}
}
finally
{
XlCall.Excel(XlCall.xlcEcho, true);
}
}
// Most of this from the newsgroup: http://groups.google.com/group/exceldna/browse_thread/thread/a72c9b9f49523fc9/4577cd6840c7f195
private static readonly TimeSpan BackoffTime = TimeSpan.FromSeconds(1);
static void AsyncRunMacro(string macroName)
{
// Do this on a new thread....
Thread newThread = new Thread(delegate()
{
while (true)
{
try
{
RunMacro(macroName);
break;
}
catch (COMException cex)
{
if (IsRetry(cex))
{
Thread.Sleep(BackoffTime);
continue;
}
// TODO: Handle unexpected error
return;
}
catch (Exception ex)
{
// TODO: Handle unexpected error
return;
}
}
});
newThread.Start();
}
static void RunMacro(string macroName)
{
object xlApp=null;
try
{
xlApp = ExcelDnaUtil.Application;
xlApp.GetType().InvokeMember("Run", BindingFlags.InvokeMethod, null, xlApp, new object[] { macroName });
}
catch (TargetInvocationException tie)
{
throw tie.InnerException;
}
finally
{
Marshal.ReleaseComObject(xlApp);
}
}
const uint RPC_E_SERVERCALL_RETRYLATER = 0x8001010A;
const uint VBA_E_IGNORE = 0x800AC472;
static bool IsRetry(COMException e)
{
uint errorCode = (uint)e.ErrorCode;
switch (errorCode)
{
case RPC_E_SERVERCALL_RETRYLATER:
case VBA_E_IGNORE:
return true;
default:
return false;
}
}
}
}
Convert your 1D array to 2D array and call resize method.
object[,] retArray = new object[a.Length, 1];
for (int i = 0; i < a.Length; i++)
{
retArray[i, 0] = a[i];
}
var ab = XlCall.Excel(XlCall.xlUDF, "Resize", retArray);
return ab;
A very strange thing occured in my program. Here is the simplified code.
class Program
{
static void Main(string[] args)
{
ArrayList numbers = new ArrayList();
numbers.Add(1);
numbers.Add(3);
numbers.Add(4);
numbers.Add(2);
var it = Sorts.MergeSort((ArrayList)numbers.Clone());
Sorts.PrintArray(it, "mergesort");
Console.WriteLine("DONE");
Console.ReadLine();
}
}
public static class Sorts
{
public static ArrayList BubbleSort(ArrayList numbers)
{
bool sorted = true;
for (int i = 0; i < numbers.Count; i++)
{
for (int j = 1; j < numbers.Count; j++)
{
if ((int)numbers[j - 1] > (int)numbers[j])
{
int tmp = (int)numbers[j - 1];
numbers[j - 1] = numbers[j];
numbers[j] = tmp;
sorted = false;
}
}
if (sorted)
{
return numbers;
}
}
return numbers;
}
public static ArrayList MergeSort(ArrayList numbers, int switchLimit = 3)
{
//if I use this if - everything works
if (numbers.Count <= 1)
{
// return numbers;
}
//the moment I use this condition - it throws SystemInvalidOperationException in function Merge in the line of a "for"-loop
if (numbers.Count <=switchLimit)
{
return Sorts.BubbleSort(numbers);
}
ArrayList ret = new ArrayList();
int middle = numbers.Count / 2;
ArrayList L = numbers.GetRange(0, middle);
ArrayList R = numbers.GetRange(middle, numbers.Count - middle);
L = MergeSort(L);
R = MergeSort(R);
return Merge(L, R);
}
private static ArrayList Merge(ArrayList L, ArrayList R)
{
ArrayList ret = new ArrayList();
int l = 0;
int r = 0;
for (int i = 0; i < L.Count + R.Count; i++)
{
if (l == L.Count)
{
ret.Add(R[r++]);
}
else if (r == R.Count)
{
ret.Add(L[l++]);
}
else if ((int)L[l] < (int)R[r])
{
ret.Add(L[l++]);
}
else
{
ret.Add(R[r++]);
}
}
return ret;
}
//---------------------------------------------------------------------------------
public static void PrintArray(ArrayList arr, string txt = "", int sleep = 0)
{
Console.WriteLine("{1}({0}): ", arr.Count, txt);
for (int i = 0; i < arr.Count; i++)
{
Console.WriteLine(arr[i].ToString().PadLeft(10));
}
Console.WriteLine();
System.Threading.Thread.Sleep(sleep);
}
}
There is a problem with my Sorts.MergeSort function.
When I use it normally (take a look at the first if-condition in a function - all works perfectly.
But the moment when I want it to switch to bubblesort with smaller input (the second if-condition in the function) it throws me an SystemInvalidOperationException. I have no idea where is the problem.
Do you see it?
Thanks. :)
Remark: bubblesort itself works - so there shouldn't be a problem in that sort...
The problem is with your use of GetRange:
This method does not create copies of the elements. The new ArrayList is only a view window into the source ArrayList. However, all subsequent changes to the source ArrayList must be done through this view window ArrayList. If changes are made directly to the source ArrayList, the view window ArrayList is invalidated and any operations on it will return an InvalidOperationException.
You're creating two views onto the original ArrayList and trying to work with both of them - but when one view modifies the underlying list, the other view is effectively invalidated.
If you change the code to create copies of the sublists - or if you work directly with the original list within specified bounds - then I believe it'll work fine.
(As noted in comments, I'd also strongly recommend that you use generic collections.)
Here's a short but complete program which demonstrates the problem you're running into:
using System;
using System.Collections;
class Program
{
static void Main()
{
ArrayList list = new ArrayList();
list.Add("a");
list.Add("b");
ArrayList view1 = list.GetRange(0, 1);
ArrayList view2 = list.GetRange(1, 1);
view1[0] = "c";
Console.WriteLine(view2[0]); // Throws an exception
}
}
on this line R = MergeSort(R); you alter the range of numbers represented by L. This invalidates L. Sorry I have to go so can't explain any further now.
In a program I'm writing, I've decided to use my own data types instead of any other database ( for educational purposes).
The data is saved as csv files, and i have a method to convert from .csv to a simple string[,].
Each class X as it's own string[,] to List converter.
After the first 3 classes i started to play with generics and reflection.
I did succeed converting a general List to string[,], but doing the opposite is started to look hard.
the way i implement List to string[,] is:
public string[,] ToArray(object[] sender)
{
if (sender.Length==0)
{
return null;
}
string[,] ret;
PropertyInfo[] props;
if (sender.Length > 0)
{
props = sender[0].GetType().GetProperties();
ret = new string[props.Length, sender.Length];
}
else
{
return null;
}
for (int i=0;i<sender.Length;i++)
{
for (int p = 0; p < props.Length; p++)
{
object value=props[p].GetValue(sender[i], null);
if (value!=null) ret[p, i] = value.ToString();
}
}
return ret;
}
and for lets say class Windows( string Name, double Size, bool Blinds)
i convert array[,] to Windows ( very generally ) like this :
public static List<Windows> ToList(string[,] arra)
{
List<Windows> ret = new List<Windows>(); // change Windows to anything
int col = array.GetLength(1);
int row = array.GetLength(0);
PropertyInfo[] props=PropArray(new Windows());
int propslen=props.Length;
for (int c = 0; c < col; c++)
{
Windows entry=new Windows();
for (int r = 0; r < propslen; r++)
{
Type pt = props[r].PropertyType;
if (pt==typeof(string))
props[r].SetValue(entry,array[r,c],null);
else
if (pt==typeof(int))
{
int i=0;int.TryParse(array[r,c],out i);
props[r].SetValue(entry,i,null);
}
else
if (pt==typeof(bool))
{
bool i = false; bool.TryParse(array[r, c], out i);
props[r].SetValue(entry,i,null);
}
else
if (pt == typeof(double))
{
double i = 0; double.TryParse(array[r, c], out i);
props[r].SetValue(entry, i, null);
}
else
if (pt==typeof(DateTime))
{
DateTime i = DateTime.MinValue; DateTime.TryParse(array[r, c], out i);
props[r].SetValue(entry,i,null);
}
}
ret.Add(entry);
}
return ret;
}
All i need to do is FindReplace the word "Windows" to any other datatype and it works.
The real question is how can i generalize it to receive a type and make a list of instances it all by itself?
Is it even possible?
Thanks in advance,
Gabriel
I will not be foolproof, but you can use generics. Something like:
public static List<T> ToList<T>(string[,] arra) // T can be anything
where T : new() // as long as it has a default constructor
{
List<T> ret = new List<T>();
int col = array.GetLength(1);
int row = array.GetLength(0);
PropertyInfo[] props=PropArray(new T());
int propslen=props.Length;
for (int c = 0; c < col; c++)
{
T entry=new T();
// ...
What you are doing is essentially a serialization format. Serialization formats are relatively simple if objects are values without complex references. Simple xml or json are common formats. Perhaps you should look into using json?
If you go for "roll your own", why not try to use property names as keys? Prefix the key/value list with the class name and instantiate the objects when you deserialize. Example format
MyApplication.Person, MyAssembly
Name=Steve
Age=30
MyApplication.Person, MyAssembly
Name=Bob
Age=54
To deserialize, read the class name, instatiate using FormatterServices.GetUninitializedObject() to get an empty instance of the class. Then using the property infos of that class, set the values from the key/value pairs that follow in the file. This is essentially a simplified JSON notation (does not support lists,maps etc.)