2013-10-25 5 views
2

Прежде всего позвольте мне сказать, что я относительный новичок на C#, и я, вероятно, слишком быстро погружаюсь слишком быстро - но мне нравится бросать вызов себе, и так я узнаю (быстро)Вызов неуправляемого кода с указателем (обновлено)

У меня есть проект, который вызывает вызовы неуправляемой dll C++. Обертка и большинство вызовов работают нормально, поэтому я знаю, что у меня есть основная структура того, как все связывается вместе, но есть один конкретный вызов, который дает мне возможность. Для вызова 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_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 

Вот 2-й ructures, что я определил в 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 в структуры внешне ... Вот отрывок из вызова метода

 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, как это (без внутреннего списка). Мое рассуждение заключается в том, что мне теперь не нужно инициализировать объект, и я мог бы просто сделать несколько вызовов к dll с NumOfParams = 1, а не NumOfParams> 1 и с 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 я получаю ERRO r: Попытка чтения или записи защищенной памяти. Это часто свидетельствует о том, что другая память повреждена.

Я знаю, что это глоток, и я действительно даже не знаю, что именно спросить, поскольку я уверен, что в этом посте есть несколько вопросов, но есть идеи о том, чтобы меня на правильном пути?

Я пробовал так много вещей на данный момент Я в недоумении, и мне просто нужно какое-то направление. Я не ищу ответ типа «сделай это для меня», а скорее объяснение и, возможно, некоторые указатели на это.Как и во всех случаях, я уверен, что есть несколько способов решения этой задачи - возможно, это работает, но не является хорошей формой и более сложным способом, который может быть «лучшей практикой»

Все предложения/комментарии будут весьма признательны. И если я исключил какие-либо релевантные данные, которые необходимы, чтобы помочь мне решить эту загадку, дайте мне знать, и я предоставлю все, что смогу.


Я хочу поблагодарить ответы до сих пор. Я пробовал каждую комбинацию, чтобы попытаться решить это сам, но до сих пор мне не повезло. Я нашел немало способов, которые НЕ работают, однако :-)

Я пробовал различные комбинации «небезопасных» - «Маршал», «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;    
     }  
    } 
} 

Вот класс, содержащий определения для структур Я использую

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 через обертку

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 будет действительно очень благодарен за любую помощь или совет предложили

Благодаря

-Ли

+0

Вы пропускаете памятку ry с 'IntPtr input = Marshal.AllocHGlobal (Marshal.SizeOf (config));', вы выходите из функции и никогда не де-alocate ее, вы должны либо сделать 'try/finally', и поставить [FreeHGlobal] (http: //msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.freehglobal.aspx) в конце или введите 'input' класс, полученный из [SafeHandle] (http://msdn.microsoft. com/en-us/library/system.runtime.interopservices.safehandle.aspx) и поместите освобождение в [ReleaseHandle] (http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle .releasehandle.aspx) –

ответ

-1

Я начал другую нить, потому что я просил неправильный вопрос из-за моей неопытности

Рабочий раствор здесь

Marshal array of struct and IntPtr

Спасибо за помощь

-Lee

0

Для инициализации списка вы просто должны добавить строку:

myConfig.sconfig = new List<SConfig>() 

, прежде чем начать добавление элементов в нее.

+0

Спасибо - я должен был знать это, но я думаю, что работал слишком поздно, и мой мозг был обжарен. – mleega

3

Вы не можете отправить маршал <>, это должен быть массив. Массив уже маршалирован как указатель, поэтому вам не нужно делать ничего особенного. Легко переходите на Pack, нет необходимости в ключевом слове unsafe.

Вы можете добавить конструктор в структуру, чтобы упростить ее инициализацию из списка <>. Как это:

[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); 
    } 
} 
+0

Спасибо, это была одна из тех областей, с которыми я боролся. Я попробую это предложение позже сегодня вечером. – mleega

Смежные вопросы