#pragma once
using namespace System;
using namespace System::Runtime;
using namespace System::Runtime::InteropServices;
using namespace Autodesk::AutoCAD::Runtime;
using namespace Autodesk::AutoCAD::DatabaseServices;
#include "dynprops.h"
namespace Autodesk
{
namespace AutoCAD
{
namespace Windows
{
namespace OPM
{
[InteropServices::Guid("d0f45feb-71d5-44ea-B1A0-6E2F27B2085D")]
[InteropServices::InterfaceTypeAttribute(InteropServices::ComInterfaceType::InterfaceIsIUnknown)]
[InteropServices::ComVisible(true)]
public interface class IOPMPropertyExpander2
{
// 获得元素值
// DISPID dispID
// IUnknown * pUnk
// DWORD dwCookie
// VARIANT * pVarOut
void GetElementValue(
[InteropServices::In] System::Int32 dispID,
[InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
[InteropServices::In] System::UInt32 dwCookie,
[InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr< Object^> pVarOut
);
// 设置元素值
// DISPID dispID
// IUnknown * pUnk
// DWORD dwCookie
// VARIANT VarIn
void SetElementValue(
[InteropServices::In] System::Int32 dispID,
[InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
[InteropServices::In] System::UInt32 dwCookie,
[InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr< Object^> VarIn
);
// 获取元素分组
// DISPID dispID
// IUnknown * pUnk
// short * groupingNumber
void GetElementGrouping(
[InteropServices::In] System::Int32 dispID,
[InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
[InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::I2)] interior_ptr<short> groupingNumber
);
// 获取组个数
// DISPID dispID
// IUnknown * pUnk
// long * nGroupCnt
void GetGroupCount(
[InteropServices::In] System::Int32 dispID,
[InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object^ pUnk,
[InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::I8)] interior_ptr<long> nGroupCnt
);
// 获取元素字符串
// DISPID dispID
// IUnknown * pUnk
// OPMLPOLESTR __RPC_FAR * pCaStringsOut
// OPMDWORD __RPC_FAR * pCaCookiesOut
void GetElementStrings(
[InteropServices::In] System::Int32 dispID,
[InteropServices::In, InteropServices::MarshalAs(InteropServices::UnmanagedType::IUnknown)] Object pUnk,
[InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr<OPMLPOLESTR> pCaStringsOut,
[InteropServices::In, InteropServices::Out, InteropServices::MarshalAs(InteropServices::UnmanagedType::Struct)] interior_ptr<OPMDWORD> pCaCookiesOut
);
};
//typedef public struct tagOPMLPOLESTR
//{
// ULONG cElems;
// /* [size_is] */ LPOLESTR __RPC_FAR* pElems;
//} OPMLPOLESTR;
//typedef struct tagOPMLPOLESTR __RPC_FAR* LPOPMLPOLESTR;
//typedef public struct tagOPMDWORD
//{
// ULONG cElems;
// /* [size_is] */ DWORD __RPC_FAR* pElems;
//} OPMDWORD;
//typedef struct tagOPMDWORD __RPC_FAR* LPOPMDWORD;
}
}
}
}
#region 自定义属性
[
Guid("EC0AB7F1-5D92-4FF1-96AE-7AC537C1347E"),
ProgId("OPMNETSample.ExpanderProperty.1"),
// No class interface is generated for this class and
// no interface is marked as the default.
// Users are expected to expose functionality through
// interfaces that will be explicitly exposed by the object
// This means the object can only expose interfaces we define
ClassInterface(ClassInterfaceType.None),
// Set the default COM interface that will be used for
// Automation. Languages like: C#, C++ and VB allow to
//query for interface's we're interested in but Automation
// only aware languages like javascript do not allow to
// query interface(s) and create only the default one
ComDefaultInterface(typeof(IDynamicProperty2)),
ComVisible(true)
]
public class ExpanderPropertyDemo : IDynamicProperty2, ICategorizeProperties, IOPMPropertyExpander2
{
private IDynamicPropertyNotify2 property_notify_ = null;
private int m_numberOfVertices;
private Point3d[] vertices;
private int currrntIndex = 0; // Save the current vertex's index
// 默认构造函数
public ExpanderPropertyDemo()
{
m_numberOfVertices = 5;
vertices = new Point3d[m_numberOfVertices];
vertices[0] = new Point3d(1000, 1000, 0);
vertices[1] = new Point3d(2000, 2000, 0);
vertices[2] = new Point3d(3000, 3000, 0);
vertices[3] = new Point3d(4000, 4000, 0);
vertices[4] = new Point3d(5000, 5000, 0);
}
void IDynamicProperty2.Connect(object pSink)
{
property_notify_ = (IDynamicPropertyNotify2)pSink;
}
void IDynamicProperty2.Disconnect()
{
property_notify_ = null;
}
void ICategorizeProperties.GetCategoryName(int propcat, uint lcid, out string pbstrName)
{
pbstrName = "扩展属性";
}
void IDynamicProperty2.GetCurrentValueData(object pUnk, ref object varData)
{
varData = null;
}
void IDynamicProperty2.GetCurrentValueName(out string name)
{
name = null;
}
void IDynamicProperty2.GetCurrentValueType(out ushort pVarType)
{
pVarType = 12; // VT_VARIANT = 12
}
void IDynamicProperty2.GetDescription(out string description)
{
description = "扩展动态属性演示";
}
void IDynamicProperty2.GetDisplayName(out string name)
{
name = "当前顶点";
}
void IOPMPropertyExpander2.GetElementGrouping(int dispID, object pUnk, ref short groupingNumber)
{
groupingNumber = 3;
}
void IOPMPropertyExpander2.GetElementValue(int dispID, object pUnk, uint dwCookie, ref object pVarOut)
{
int index = (int)dwCookie / 3;
int subIndex = ((int)dwCookie - index * 3) % 3;
pVarOut = vertices[index][subIndex];
currrntIndex = index;
}
void IOPMPropertyExpander2.GetGroupCount(int dispID, object pUnk, ref int nGroupCnt)
{
nGroupCnt = m_numberOfVertices;
}
void IDynamicProperty2.GetGUID(out Guid propGUID)
{
propGUID = new Guid("9CAF41C2-CA86-4ffb-B05A-AC43C424D076");
}
void IDynamicProperty2.IsPropertyEnabled(object pUnk, out int bEnabled)
{
bEnabled = 1;
}
void IDynamicProperty2.IsPropertyReadOnly(out int bReadonly)
{
bReadonly = 0;
}
void ICategorizeProperties.MapPropertyToCategory(int dispid, out int ppropcat)
{
ppropcat = 4;
}
void IDynamicProperty2.SetCurrentValueData(object pUnk, object varData)
{
/*
* when pick a point, this method is called.
*/
double[] xyz = (double[])varData;
vertices[currrntIndex] = new Point3d(xyz[0], xyz[1], xyz[2]);
}
void IOPMPropertyExpander2.SetElementValue(int dispID, object pUnk, uint dwCookie, ref object VarIn)
{
int index = (int)dwCookie / 3;
int subIndex = ((int)dwCookie - index * 3) % 3;
Point3d point = vertices[index];
if (subIndex == 0)
{
vertices[index] = new Point3d((double)VarIn, point.Y, point.Z);
}
else if (subIndex == 1)
{
vertices[index] = new Point3d(point.X, (double)VarIn, point.Z);
}
else if (subIndex == 2)
{
vertices[index] = new Point3d(point.X, point.Y, (double)VarIn);
}
}
/* public unsafe void GetElementStrings(
[In] int dispID,
[In, MarshalAs(UnmanagedType.IUnknown)] object pUnk,
[In, Out, MarshalAs(UnmanagedType.Struct)] ref OPMLPOLESTR pCaStringsOut,
[In, Out, MarshalAs(UnmanagedType.Struct)] ref OPMDWORD pCaCookiesOut)
{
List<string> strings = new List<string> { "顶点 X 坐标", "顶点 Y 坐标", "顶点 Z 坐标" };
List<uint> values = new List<uint> { 0, 1, 2 };
pCaStringsOut = OPMUtils.CreateOPMLPOLESTR(strings);
pCaCookiesOut = OPMUtils.CreateOPMDWORD(values);
}*/
public void Initialize()
{
ExpanderPropertyDemo epd = new ExpanderPropertyDemo();
}
public void Terminate()
{
throw new NotImplementedException();
}
}
#endregion
一种结果是显示不支持语法,
另一种结果是实现接口后,还是出现错误
C#调用C++的结构体,以接口类的形式调用
尝试过网络一些C#调用C++的结构体方式,显示还是报错,导入C++结构体,是后面再指向C++的结构体,而接口实现,则是开始就指向C++的结构体
希望能有一个好的解决方案,能让代码通过测试
C++ dll接口定义,举个例子
struct User
{
char userId[64];
char username[32];
char password[64];
};
DLL_EXPORT int STDCALL API_UpdateOneUser
(
IN User *user
);
C#数据结构及接口定义
[StructLayout(LayoutKind.Sequential)]
public struct User
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] userId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] username;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] password;
}
const string DLL_PATH = "mydll.dll";
[DllImport(DLL_PATH, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
static extern int API_UpdateOneUser(ref User user);
//以下是接口调用
User user = new User();
user.userId = ...
user.username = ...
int result = API_UpdateOneUser(ref user);
typedef struct
{
char name[20];
int age;
double scores[32];
}Student;
//Class中包含结构体数组类型
typedef struct
{
int number;
Student stedents[50];
}Class;
JNAAPI int GetClass(Class *pClass,int len)
{
for(int i = 0; i < len; i++)
{
pClass[i].number = i;
for(int j = 0; j< 50; j++)
{
//把name中的前20个字节用0代替
memset(pClass[i].stedents[j].name, 0, 20);
//给每个同学命名
sprintf(pClass[i].stedents[j].name, "name_%d_%d", i, j);
pClass[i].stedents[j].age = j % 2 == 0 ? 15:20;
}//for
}//for
return 0;
}
上面DLL 的导出函数要求传递的参数为它自定义的Class结构体数组, 那么我们在C#调用它时也要自定义对应的结构体了,
我们可以定义为如下:
[StructLayout(LayoutKind.Sequential)]
struct Student
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string name;
public int age;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public double[] scores;
}
[StructLayout(LayoutKind.Sequential)]
struct Class
{
public int number;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)]
public Student[] students;
}
需要注意的是,这2个结构体中的数组大小一定要跟C++中的限定一样大小哦,接下来如何使用这个API来正确的获取数据呢,大多数人可能想到像这样的处理方式:
Class myclass = new Class();
IntPtr ptr=Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Class)));
GetClass(ptr);
Marshal.FreeHGlobal(ptr);
没错,这样的处理是没问题的,但是我们的API的参数是Class数组,这种处理方式只是传递一个Class结构体参数,所以这种方式在这里就不太合适了,!
那大家就想到先Class[] myclass = new Class[MaxClass]; 然后在用Marshal.AllocHGlobal 来获取myclass 数据的指针,
其实这样也是错的, 因为 Class结构中包含了,不能直接封送的Student结构,所以无论如何上面的想法是错误的!
那要怎么办呢,其实很简单,就是先分配一段非托管内存,并调用API后,再将非托管内容数据读取到托管结构体数据中!
示例演示代码如下:
// 接口定义
[DllImport("CSharpInvokeCpp_CppDemo.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetClass(IntPtr pv, int len);
//复杂结构体传递测试代码
int size = Marshal.SizeOf(typeof(Class)) * 50;
IntPtr pBuff = Marshal.AllocHGlobal(size);
CppDLL.GetClass(pBuff, 50);
Class[] pClass = new Class[50];
for (int i = 0; i < 50; i++)
{
IntPtr pr = new IntPtr(pBuff.ToInt64() + Marshal.SizeOf(typeof(Class)) * i);
pClass[i] = (Class)Marshal.PtrToStructure(pr, typeof(Class));
}
Marshal.FreeHGlobal(pBuff);