使用指针调用非托管代码 [英] Calling un-managed code with pointer

查看:55
本文介绍了使用指针调用非托管代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 C# 项目,它调用非托管 C++ dll.包装器和大多数调用工作正常,所以我知道我有一切如何联系在一起的基本结构,但有一个特定的调用让我适合.API 调用需要一个指向结构的指针,该结构包含配置数据列表.

I have a C# project that makes calls out to an unmanaged C++ dll. The wrapper and most of the calls are working OK, so I know that I have the basic structure of how everything ties together OK, but there is one specific call that is giving me fits. The API call requires a pointer to a structure, which contains a list of configuration data.

这是电话:

m_status = m_XXXXBox.SetConfig(m_channelId, ref SCONFIG_LIST);

其中 SCONFIG_LIST 是包含数据的结构...

Where SCONFIG_LIST is the structure containing the data...

该问题特别与 SCONFIG_LIST 相关

The issue specifically relates to SCONFIG_LIST

这里是直接来自此 API 规范的文档:

Here is the documentation directly from the spec for this 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_LIST
where:
NumOfParms is an INPUT, which contains the number of SCONFIG elements in the array
pointed 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 个结构:

Here are the 2 structures that I defined in C#:

[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...这是调用方法的片段

I know you can not have field initializers in structs, but I cant seem to figure out how to initialize the sconfig in the struct externally... Here is snippet from the calling method

      SConfig_List myConfig = new SConfig_List();
      SConfig configData = new SConfig();

      configData.Parameter = 0x04;
      configData.Value = 0x10;
      myConfig.NumOfParams = 1;
      myConfig.sconfig.Add(configData);

这会在运行时引发错误对象引用未设置为对象的实例"我理解这个错误,因为 sconfig 尚未初始化 - 我只是不知道如何做到这一点......

This throws an error at runtime of "object reference not set to an instance of an object" I understand this error because sconfig has not been initialized - I just cant figure out how to do that....

所以我的下一个想法是解决这个问题,我会像这样创建 SCONFIG_LIST 结构(里面没有列表) - 我的理由是我现在不必初始化对象,我可以使用 NumOfParams = 1 多次调用 dll,而不是 NumOfParams > 1 并让 dll 循环遍历结构数据.

So my next thought was to get around this, I would just create the SCONFIG_LIST struct like this (without the list inside) - My reasoning for this is that I now do not have to initialize the object, and I could just make multiple calls to dll with a NumOfParams = 1, rather than NumOfParams > 1 and having the dll loop through the struct data.

[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的实际方法围绕编组仍有几个问题/问题,但这里是:

This got rid of the errors to this point, now on the the actual method that calls the dll There are still several questions / issues surrounding the Marshalling, but here it is:

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 时,出现错误:尝试读取或写入受保护的内存.这通常表明其他内存已损坏.

This gets past all of the initial setup without error, but when I try to actually invoke the dll I get an error : Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

我知道这很啰嗦,我什至不知道该问什么,因为我确信这篇文章中存在多个问题,但是有没有让我走上正轨的想法?

I know this is a mouthful, and I really don't even know exactly what to ask as I am sure there are multiple issues within this post, but any ideas on getting me on the right track?

此时我已经尝试了很多东西,我不知所措,我只需要一些指导.我不是在寻找为我做这件事"类型的答案,而是寻找一个解释,也许还有一些关于完成这项工作的指示.与所有事情一样,我确信有多种方法可以完成任务——也许一种有效但不是好的形式,还有一种可能是更好的实践"的更长更复杂的方法

I have tried so many things at this point I am at a loss, and I just need some direction. I am not looking for a "do this for me" type answer, but rather an explanation and maybe some pointers on getting this done. As with all things, I am sure there are multiple ways of accomplishing the task - maybe a way that works, but is not good form, and a longer more complicated way that may be "better practice"

任何和所有建议/意见将不胜感激.如果我排除了帮助我解决这个谜语所需的任何相关数据,请告诉我,我会尽我所能.

Any and all suggestions / comments will be greatly appreciated. And if I excluded any relevant data that is required to help me solve this riddle let me know and I will provide what I can.

我要感谢到目前为止的回复.我一直在尝试各种组合来尝试自己解决这个问题,但到目前为止我还没有任何运气.但是,我发现很多方法都行不通:-)

I want to thank the responses so far. I have been trying every combination to try and resolve this myself but I have not had any luck so far. I have found quite a few ways that do NOT work, however :-)

我尝试了各种不安全"的组合——MarshalAs"、StructLayout"以及我在网上找到的其他一些东西,现在我求饶了.

I have tried various combinations of "unsafe" - "MarshalAs", "StructLayout" and several other things I found on the web, now I am begging for mercy.

我已经成功地实现了对这个非托管 dll 的其他几个调用,但它们都使用简单的整数指针等.我的问题是将指针传递给包含另一个结构数组的 Struct.如果您查看我的原始问题的最顶部,您可以看到 dll 中的文档以及它希望事物的结构.没有返回值,我只是想通过这个 dll 将一些配置设置传递给设备.

I have successfully implemented several other calls to this unmanaged dll, but all of them use simple integer pointers etc. My problem is passing the pointer to a Struct containing an array of another struct. If you look at the very top of my original question you can see the documentation from the dll and how it wants thing structured. There is NO return value, I am merely trying to pass some configuration settings to a device through this dll.

我将发布我整个项目的框架,这样也许我可以让某人在此过程中牵我的手,并希望在未来也能帮助其他人尝试解决此类问题.

I am going to post a framework of my entire project so that maybe I can get someone to hold my hand through this process, and hopefully help others in the future trying to solve this type of issue as well.

这是 Wrapper 的骨架(未显示所有功能)

Here is skeleton of Wrapper (not all functions displayed)

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 的方式不正确 - 但这显示了我正在尝试做的事情

The actual Class containing the API Code - this is where the error is, I know that how I have the IntPtrs now is Not correct - But this displays what I am trying to do

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

这是包含我正在使用的结构的定义的类

Here is class containing the definitions for the Structs I am using

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

最后 - 实际应用程序通过包装器调用 dll

And finally - the actual application calling the dll through the wrapper

using 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;
        }
    }
}

推荐答案

我开始了另一个话题,因为我缺乏经验,问错了问题

I started another thread because I was asking the wrong question due to my inexperience

有效的解决方案在这里

结构体和 IntPtr 的编组数组

感谢您的帮助

-李

这篇关于使用指针调用非托管代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆