Since P/Invoke does not support returning dynamic sized arrays (you must statically specify the size of the array at compile time), I decided to write a C++/CLI wrapper for some functions I need in a .net application that is otherwise written in C#.
Take the GetTcpTable2 function from IpHlpApi.dll...
I made C# classes to match the types in that function as follows:
public class MibTcpTable2
{
public int NumEntries;
public MibTcpRow2[] Table;
}
public class MibTcpRow2
{
public int State;
public int LocalAddr;
public int LocalPort;
public int RemoteAddr;
public int RemotePort;
public int OwningPid;
public int OffloadState;
}
In my C++/CLI program, I call GetTcpTable2 as shown in the MSDN example, and then iterate through the resulting array and assign its output to a new instance of the TcpTable2 class I made in C#.
See code:
PMIB_TCPTABLE2 pTcpTable;
ULONG ulSize = 0;
DWORD dwRetVal = 0;
pTcpTable = (MIB_TCPTABLE2 *)MALLOC(sizeof(MIB_TCPTABLE2));
if (pTcpTable == NULL) {
return nullptr;
}
ulSize = sizeof(MIB_TCPTABLE);
if ((dwRetVal = ::GetTcpTable2(pTcpTable, &ulSize, TRUE)) == ERROR_INSUFFICIENT_BUFFER)
{
FREE(pTcpTable);
pTcpTable = (MIB_TCPTABLE2 *)MALLOC(ulSize);
if (pTcpTable == NULL) {
return nullptr;
}
}
NetClasses::MibTcpTable2^ managedTable = gcnew NetClasses::MibTcpTable2();
managedTable->Table = gcnew cli::array<NetClasses::MibTcpRow2^>(pTcpTable->dwNumEntries);
if ((dwRetVal = ::GetTcpTable2(pTcpTable, &ulSize, TRUE)) == NO_ERROR)
{
for (int i = 0; i < pTcpTable->dwNumEntries; i++)
{
managedTable->Table[i].LocalAddr = pTcpTable->table[i].dwLocalAddr;
managedTable->Table[i].LocalPort = pTcpTable->table[i].dwLocalPort;
managedTable->Table[i].OffloadState = pTcpTable->table[i].dwOffloadState;
managedTable->Table[i].OwningPid = pTcpTable->table[i].dwOwningPid;
managedTable->Table[i].RemoteAddr = pTcpTable->table[i].dwRemoteAddr;
managedTable->Table[i].RemotePort = pTcpTable->table[i].dwRemotePort;
managedTable->Table[i].State = pTcpTable->table[i].dwState;
}
}
However, Visual Studio 2015 hates the accesses to managedTable inside the for loop. It complains that "expression must have a class type." Ok, so that usually means you're using the wrong data accessor operator, so I tried a dot instead. No dice.
How the heck do I access the Table member of managedTable? The access to it before the for loop was valid. Why isn't it valid inside the for loop?
Your array access is giving you a handle to a managed object, so shouldn't your field access also be -> rather than . ?
Array[i]->Field
Related
For context i'm tring to override this function:
public override unsafe int SearchInTarget(string text)
{
int num = 0;
byte[] bytes = Helpers.GetBytes(text ?? string.Empty, Encoding, zeroTerminated: false);
fixed (byte* value = bytes)
{
num = DirectMessage(2197, new IntPtr(bytes.Length), new IntPtr(value)).ToInt32();
}
if (num == -1)
{
return num;
}
return Lines.ByteToCharPosition(num);
}
Because it always returns the first element it finds, I'm trying to change that.
First of all, I don't know how to work with unsafe functions, but setting that apart, this function has Helpers class, DirectMessage function, inside the package and if I try to override it says they're missing.
Is there a way to override with the compiler knowing that I'm referring to those class?
Best way would be if I could change the package directly. :)
I am working with convergent facet bodies in NX and using C# in NXOpen. In the process, I am using UFSession.Facet.AskAdjacentFacet function to get the adjacent facets of each facet. But on using this particular command, the NXOpen throws error stating "NXOpen.NXException: The facet object is not supported for this operation". I went through the example given in NXOpen documentation (https://docs.plm.automation.siemens.com/data_services/resources/nx/10/nx_api/en_US/custom/ugopen_doc/uf_facet/uf_facet_eg2.c) and used similar approach, but this error shows up any way. Below is the script that I tried.
'''
public static void Main(string[] args)
{
NXOpen.UF.UFFacet myFacet = UFSession.Facet;
int facetID;
int edgeID;
int adjFacID;
int edgeIDinAdjFac;
int null_facet_ID = UFConstants.UF_FACET_NULL_FACET_ID;
facetID = null_facet_ID;
foreach (NXOpen.Facet.FacetedBody facetBody in workPart.FacetedBodies)
{
myFacet.CycleFacets(facetBody.Tag, ref facetID); // initialise for cycling
while (facetID != null_facet_ID)
{
List<int> Adj_fac_list = new List<int>();
for (edgeID = 0; edgeID < 3; edgeID++)
{
myFacet.AskAdjacentFacet(facetBody.Tag, facetID, edgeID, out adjFacID, out edgeIDinAdjFac);
if (adjFacID != UFConstants.UF_FACET_NULL_FACET_ID)
{
Adj_fac_list.Add(adjFacID);
}
}
}
}
}
Note: I could use the same model tag and facet id in the function UFSession.FACET.AskNumVertsInFacet and the script works fine. But I do not know why AskAdjacentFacet is not working. Can anyone help me on why there is an error and how to get this working?
On first glimpse, the problem I see is that you have not initialized the variable myFacet and it's null. And since it's null, you cannot call its members.
So change a single line of the code from
NXOpen.UF.UFFacet myFacet = UFSession.Facet;
To
NXOpen.UF.UFFacet myFacet = UFSession.GetUFSession().Facet;
I apologize in advance. My domain is mostly C (and C++). I'm trying to write something similar in C#. Let me explain with code.
In C++, I can use large static arrays that are processed during compile-time and stored in a read-only section of the PE file. For instance:
typedef struct _MY_ASSOC{
const char* name;
unsigned int value;
}MY_ASSOC, *LPMY_ASSOC;
bool GetValueForName(const char* pName, unsigned int* pnOutValue = nullptr)
{
bool bResult = false;
unsigned int nValue = 0;
static const MY_ASSOC all_assoc[] = {
{"name1", 123},
{"name2", 213},
{"name3", 1433},
//... more to follow
{"nameN", 12837},
};
for(size_t i = 0; i < _countof(all_assoc); i++)
{
if(strcmp(all_assoc[i].name, pName) == 0)
{
nValue = all_assoc[i].value;
bResult = true;
break;
}
}
if(pnOutValue)
*pnOutValue = nValue;
return bResult;
}
In the example above, the initialization of static const MY_ASSOC all_assoc is never called at run-time. It is entirely processed during the compile-time.
Now if I write something similar in C#:
public struct NameValue
{
public string name;
public uint value;
}
private static readonly NameValue[] g_arrNV_Assoc = new NameValue[] {
new NameValue() { name = "name1", value = 123 },
new NameValue() { name = "name2", value = 213 },
new NameValue() { name = "name3", value = 1433 },
// ... more to follow
new NameValue() { name = "nameN", value = 12837 },
};
public static bool GetValueForName(string name, out uint nOutValue)
{
foreach (NameValue nv in g_arrNV_Assoc)
{
if (name == nv.name)
{
nOutValue = nv.value;
return true;
}
}
nOutValue = 0;
return false;
}
The line private static readonly NameValue[] g_arrNV_Assoc has to be called once during the host class initialization, and it is done for every single element in that array!
So my question -- can I somehow optimize it so that the data stored in g_arrNV_Assoc array is stored in the PE section and not initialized at run-time?
PS. I hope I'm clear for the .NET folks with my terminology.
Indeed the terminology is sufficient enough, large static array is fine.
There is nothing you can really do to make it more efficient out of the box.
It will load initially once (at different times depending on which version of .net and if you have a static constructor). However, it will load before you call it.
Even if you created it empty with just the predetermined size, the CLR is still going to initialize each element to default, then you would have to buffer copy over your data somehow which in turn will have to be loaded from file.
The question are though
How much overhead does loading the default static array of struct actually have compared to what you are doing in C
Does it matter when in the lifecycle of the application when its loaded
And if this is way too much over-head (which i have already assumed you have determined), what other options are possibly available outside the box?
You could possibly pre-allocate a chunk of unmanaged memory, then read and copy the bytes in from somewhere, then inturn access using pointers.
You could also create this in a standard Dll, Pinvoke just like an other un-managed DLL. However i'm not really sure you will get much of a free-lunch here anyway, as there is overhead to marshal these sorts of calls to load your dll.
If your question is only academic, these are really your only options. However if this is actually a performance problem you have, you will need to try and benchmark this for micro-optimization and try to figure out what is suitable to you.
Anyway, i don't profess to know everything, maybe someone else has a better idea or more information. Good luck
I'm starting to dive into the world of C# Dynamics and Metaprogramming, and having some trouble.
I managed to create a CodeDom tree, and generate the following code:
namespace Mimsy {
using System;
using System.Text;
using System.Collections;
internal class JubJub {
private int _wabeCount;
private ArrayList _updates;
public JubJub(int wabeCount) {
this._updates = new ArrayList();
this.WabeCount = wabeCount;
}
public int WabeCount {
get {
return this._wabeCount;
}
set {
if((value < 0))
this._wabeCount = 0;
else
this._wabeCount = value;
this._updates.Add(this._wabeCount);
}
}
public string GetWabeCountHistory() {
StringBuilder result = new StringBuilder();
int ndx;
for(ndx = 0; (ndx < this._updates.Count); ndx = ndx + 1) {
if((ndx == 0))
result.AppendFormat("{0}", this._updates[ndx]);
else
result.AppendFormat(", {0}", this._updates[ndx]);
}
}
}
}
I am then compiling dynamically this namespace to an assembly named "dummy".
I can succesfully get an instance of this Type:
string typeName = "Mimsy.JubJub";
Type type = dummyAssembly.GetType(typeName);
dynamic obj = Activator.CreateInstance(type, new object[] { 8 });
//obj is a valid instance type
If I debug this code, I can see in the debugger that obj actually has the property WabeCount:
However, when trying to access this property, the compiler shouts that the dynamic property does not exist.
There are one or perhaps two problems with your code:
You are using an internal class, and trying to access it with dynamic. The two things don't play well together. See https://stackoverflow.com/a/18806787/613130. Use public clasas
You need to cast the value before assigning it to wabeCount, like:
obj.WabeCount = (int)wabes[ndx]
Note that technically, if your "main" assembly is strong named, you could add the InternalsVisibleToAttribute to the "dynamic" assembly to make its internal "things" visible to the main assembly... I do think it would be wasted work.
I'm currently writing a program in CLI/C++ using an imported C# package. I need to use one of the functions in one of the C# objects which takes in an array. Unfortunately it isn't allowing me to use a CLI array, defined like so:
array<float>^ knots = gcnew array<float>(nurbs->GetNumKnots());
(and then populated in a loop).
The object and the function are:
TRH::NURBSCurveKit^ nurbsKit = gcnew TRH::NURBSCurveKit();
nurbsKit->SetKnots(nurbs->GetNumKnots(), knots);
This returns an error basically saying that the cli::array type isn't compatible. Does anyone know of a way where I can cast the array, or possibly define it differently?
I'm quite new to CLI so I'm a little vague on the way it handles things at times.
Thanks
(I do something similar later on using an array of TRH::Points, but they're not defined as references or pointers, so I'm not sure if they'd work with any solutions or not).
I'm not sure if it's the same NURBSCurveKit, but according to an online API reference I found, the SetKnots method takes one parameter, not two. Since a managed array knows how long it is, you generally don't have to pass in a length with an array.
If this matches your API, just switch to pass a single parameter to SetKnots. (The reference I found uses a different namespace, so it may not be what you're using.)
array<float>^ knots = gcnew array<float>(nurbs->GetNumKnots());
TRH::NURBSCurveKit^ nurbsKit = gcnew TRH::NURBSCurveKit();
nurbsKit->SetKnots(knots);
This is my test case, seems everything is fine.
C#
namespace CS
{
public class Test
{
public int GetNum()
{
return 5;
}
public void ShowArray(int num, float[] arr)
{
for (int i = 0; i < num; i++)
{
Console.WriteLine("[{0}] = {1}",i,arr[i]);
}
}
}
}
C++/CLI
using namespace System;
using namespace CS;
int main(array<System::String ^> ^args)
{
Test^ test = gcnew Test();
array<float>^ arr = gcnew array<float>(test->GetNum());
for (int i = 0; i < test->GetNum(); i ++)
{
arr[i] = (float)i * i;
}
test->ShowArray(test->GetNum(), arr);
Console::ReadKey();
return 0;
}
Dose you code have something different form mine?