我有一个C#项目,可以调用非托管C ++ dll。包装程序和大多数调用都可以正常运行,因此我知道我具有所有组件如何正常连接的基本结构,但是有一个特定的调用适合我。 API调用需要指向结构的指针,该结构包含配置数据列表。这里是电话:m_status = m_XXXXBox.SetConfig(m_channelId, ref SCONFIG_LIST);其中SCONFIG_LIST是包含数据的结构...该问题特别与SCONFIG_LIST有关这是直接来自此API规范的文档:Points to the structure SCONFIG_LIST, which is defined as follows:typedef struct{unsigned long NumOfParams; /* number of SCONFIG elements */SCONFIG *ConfigPtr; /* array of SCONFIG */} SCONFIG_LISTwhere:NumOfParms is an INPUT, which contains the number of SCONFIG elements in the arraypointed to by ConfigPtr.ConfigPtr is a pointer to an array of SCONFIG structures.The structure SCONFIG is defined as follows:typedef struct{unsigned long Parameter; /* name of parameter */unsigned long Value; /* value of the parameter */} SCONFIG这是我在C#中定义的2种结构:[StructLayout(LayoutKind.Sequential, Pack = 1)]public unsafe struct SConfig{ public int Parameter; public int Value;}[StructLayout(LayoutKind.Sequential, Pack = 1)]public unsafe struct SConfig_List{ public int NumOfParams; // public List<SConfig> sconfig = new List<SConfig>(); // This throws compile time error public List<SConfig> sconfig;}我知道您不能在结构体中使用字段初始化程序,但是我似乎无法弄清楚如何从外部初始化结构体中的sconfig。这是调用方法的摘录 SConfig_List myConfig = new SConfig_List(); SConfig configData = new SConfig(); configData.Parameter = 0x04; configData.Value = 0x10; myConfig.NumOfParams = 1; myConfig.sconfig.Add(configData);这将在运行时引发错误“对象引用未设置为对象的实例”我了解此错误,因为sconfig尚未初始化-我只是想不出该怎么做...。所以我的下一个想法是解决这个问题,我将像这样创建SCONFIG_LIST结构(里面没有列表)-我这样做的原因是我现在不必初始化该对象,我可以对它进行多次调用NumOfParams = 1而不是NumOfParams> 1的dll,并且dll遍历结构数据。[StructLayout(LayoutKind.Sequential, Pack = 1)]public unsafe struct SConfig_List{ public int NumOfParams; public SConfig sconfig;}这就是我所说的方法configData.Parameter = 0x04;configData.Value = 0x10;myConfig.NumOfParams = 1;myConfig.sconfig.Parameter = configData.Parameter;myConfig.sconfig.Value = configData.Value;m_status = m_XXXXBox.SetConfig(m_channelId, ref myConfig);至此,现在已经消除了调用dll的实际方法上的错误有关编组的问题仍然很多,但这里是:public XXXXErr SetConfig(int channelId, ref SConfig_List config){ unsafe { IntPtr output = IntPtr.Zero; IntPtr input = Marshal.AllocHGlobal(Marshal.SizeOf(config)); Marshal.StructureToPtr(config, input, true); XXXXErr returnVal = (XXXXErr)m_wrapper.Ioctl(channelId, (int)Ioctl.SET_CONFIG, input, output); return returnVal; }}这通过了所有初始设置,没有错误,但是当我尝试实际调用dll时,我得到一个错误:尝试读取或写入受保护的内存。这通常表明其他内存已损坏。我知道这是一个大问题,我真的不知道该问什么,因为我确定这篇文章中存在多个问题,但是有什么想法可以使我走上正确的道路?在这一点上,我已经尝试了很多东西,我很茫然,我只需要一些指导。我不是在寻找“为我做这件事”类型的答案,而是在寻找一个解释,也许还有一些完成此想法的指示。与所有事物一样,我确信完成任务有多种方法-可能有效,但不是好的形式,而更长久的复杂方法可能是“更好的实践”任何和所有建议/评论将不胜感激。如果我排除了帮助解决这个难题所需的任何相关数据,请告诉我,我将尽我所能。我要感谢到目前为止的答复。我一直在尝试各种组合来尝试自己解决此问题,但到目前为止我还没有遇到任何运气。我发现了很多无效的方法,但是:-)我已经尝试过各种“不安全”-“ MarshalAs”,“ StructLayout”和我在网上找到的其他几种组合,现在我求饶。我已经成功实现了对该非托管dll的其他几个调用,但是它们全部使用简单的整数指针等。我的问题是将指针传递给包含另一个结构数组的Struct。如果您查看我原始问题的最上层,则可以查看dll中的文档以及它如何使事物结构化。没有返回值,我只是试图通过此dll将一些配置设置传递给设备。我将发布整个项目的框架,以便也许我可以帮助某个人完成此过程,并希望将来也可以帮助其他人解决此类问题。这是包装器的骨架(并非显示所有功能)using System;using System.Runtime.InteropServices;namespace My_Project{ internal static class NativeMethods { [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string dllToLoad); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); [DllImport("kernel32.dll")] public static extern bool FreeLibrary(IntPtr hModule); } internal class APIDllWrapper { private IntPtr m_pDll; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int APIIoctl(int channelId, int ioctlID, IntPtr input, IntPtr output); public APIIoctl Ioctl; //extern “C” long WINAPI APIIoctl //( //unsigned long ChannelID, //unsigned long IoctlID, //void *pInput, //void *pOutput //) public bool LoadAPILibrary(string path) { m_pDll = NativeMethods.LoadLibrary(path); if (m_pDll == IntPtr.Zero) return false; pAddressOfFunctionToCall = NativeMethods.GetProcAddress(m_pDll, "APIIoctl"); if (pAddressOfFunctionToCall != IntPtr.Zero) Ioctl = (APIIoctl)Marshal.GetDelegateForFunctionPointer( pAddressOfFunctionToCall, typeof(APIIoctl)); return true; } public bool FreeLibrary() { return NativeMethods.FreeLibrary(m_pDll); } }}And Here is the class that defines the hardware I am trying to communicate with namespace My_Project{ public class APIDevice { public string Vendor { get; set; } public string Name { get; set; } public override string ToString() { return Name; } }}Interface using System.Collections.Generic;namespace My_Project{ public interface I_API { APIErr SetConfig(int channelId, ref SConfig_List config); }}包含API代码的实际类-这是错误所在,我知道我现在拥有IntPtrs的方法不正确-但这显示了我要执行的操作using System;using System.Collections.Generic;using System.Runtime.InteropServices;namespace My_Project{ public class API : I_API { private APIDevice m_device; private APIDllWrapper m_wrapper; public APIErr SetConfig(int channelId, ref SConfig_List config) { IntPtr output = IntPtr.Zero; IntPtr input = Marshal.AllocHGlobal(Marshal.SizeOf(config)); Marshal.StructureToPtr(config, input, true); APIErr returnVal = (APIErr)m_wrapper.Ioctl(channelId, (int)Ioctl.SET_CONFIG, input, output); return returnVal; } }}这是包含我正在使用的Structs的定义的类using System;using System.Collections.Generic;using System.Runtime.InteropServices;namespace My_Project{ public enum APIErr { STATUS_NOERROR = 0x00, ERR_BUFFER_EMPTY = 0x10, ERR_BUFFER_FULL = 0x11, ERR_BUFFER_OVERFLOW = 0x12 } public struct SConfig { public int Parameter; public int Value; } public struct SConfig_List { public int NumOfParams; public SConfig[] sconfig; public SConfig_List(List<SConfig> param) { this.NumOfParams = param.Count; this.sconfig = new SConfig[param.Count]; param.CopyTo(this.sconfig); } }}最后-实际的应用程序通过包装程序调用dllusing System;using System.Collections.Generic;using System.Linq;using System.Text;using My_Project;namespace Test_API{ public class Comm { private I_API m_APIBox; APIErr m_status; int m_channelId; bool m_isConnected; public Comm(I_API apiInterface) { m_APIBox = apiInterface; m_isConnected = false; m_status = APIErr.STATUS_NOERROR; } public bool ConfigureDevice() { SConfig tempConfig = new SConfig(); tempConfig.Parameter = 0x04; tempConfig.Value = 0x10; SConfig_List setConfig = new SConfig_List(tempConfig); m_status = m_APIBox.SetConfig(m_channelId, ref setConfig); if (m_status != APIErr.STATUS_NOERROR) { m_APIBox.Disconnect(m_channelId); return false; } return true; } }} 最佳答案 您无法编组List ,它必须是一个数组。数组已经封送为指针,因此您无需执行任何特殊操作。轻松打包,无需使用不安全的关键字。您可以向该结构添加一个构造函数,以方便从List 对其进行初始化。像这样:[StructLayout(LayoutKind.Sequential)]public struct SConfig { public int Parameter; public int Value;}[StructLayout(LayoutKind.Sequential)]public struct SConfig_List { public int NumOfParams; public SConfig[] sconfig; public SConfig_List(List<SConfig> param) { this.NumOfParams = param.Count; this.sconfig = new SConfig[param.Count]; param.CopyTo(this.sconfig); }}
09-07 22:16