簡體   English   中英

struct和IntPtr的元數據數組

[英]Marshal array of struct and IntPtr

我非常接近解決問題的方法,但是我需要一些收尾指導以使一切正常進行。 在過去的一周里,我學到了很多關於

只是作為參考,我上周就相同的話題問了類似的問題,但是由於我方面的巨大監督,我提出了錯誤的問題。

我正在嘗試使用非托管的c ++ dll,該dll是用於與連接的設備進行通信的API。 我已經成功創建了包裝器以及其他大多數函數調用,但是最后一個使我發瘋。

有關某些背景信息(可能不需要回答此問題,請記住我當時的基本思維過程存在缺陷)在此處: 用指針調用非托管代碼(已更新)

在我最初的問題中,我問的是要為包含struct(2)數組的struct(1)創建一個IntPtr。...實際上,struct(1)根本不包含數組,它包含一個指向數組。

這是我嘗試實現作為參考的API文檔:

extern “C” long WINAPI PassThruIoctl
(
    unsigned long ChannelID,
    unsigned long IoctlID,
    void *pInput,
    void *pOutput
)


// *pInput Points to the structure SCONFIG_LIST, which is defined as follows:
// *pOutput is not used in this function and is a null pointer

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

這是我目前定義的結構定義

[StructLayout(LayoutKind.Sequential)] // Also tried with Pack=1
public struct SConfig 
{
   public UInt32 Parameter;
   public UInt32 Value;
}



[StructLayout(LayoutKind.Sequential)] // Also tried with Pack=1
public struct SConfig_List
{
    public UInt32 NumOfParams;
    public IntPtr configPtr;

    public SConfig_List(UInt32 nParams, SConfig[] config)
    {
        this.NumOfParams = nParams;

        //  I have tried these 2 lines together
        IntPtr temp = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyNameSpace.SConfig)) * (int)nParams);
        this.configPtr = new IntPtr(temp.ToInt32());

        // I have tried this by itself
        // this.configPtr  = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyNameSpace.SConfig)) * (int)nParams);


        // and this line
        // this.configPtr = Marshal.AllocHGlobal(sizeof(SConfig)*(int)nParams);  // this only complies with unsafe struct
    }
}

這是將這些代碼設置為變量並調用與API接口的函數的代碼段

SConfig[] arr_sconfig;
arr_sconfig = new SConfig[1];

arr_sconfig[0].Parameter = 0x04;
arr_sconfig[0].Value = 0xF1;
SConfig_List myConfig = new SConfig_List(1, arr_sconfig);

m_status = m_APIBox.SetConfig(m_channelId, ref myConfig);

最后,這是將這些信息傳遞給dll的函數:

public APIErr SetConfig(int channelId, ref SConfig_List config)
{
    unsafe
    {
        IntPtr output = IntPtr.Zero; // Output not used, just a null pointer for this function

        //   These 2 lines of code cause API dll to yell about invalid pointer (C# is happy but it doesnt work with dll)
        //   IntPtr temp = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(J_2534_API.SConfig_List)));
        //   IntPtr input = new IntPtr(temp.ToInt32());

        //  The following 2 lines only compile with unsafe - but API dll "likes" the pointer - but I am not getting desired results
        //  The dll is properly getting the Number of Parameters (NumOfParams), but the data within the array is not being
        //  referenced correctly
        IntPtr input = Marshal.AllocHGlobal(sizeof(SConfig_List)); // Only works with unsafe
        Marshal.StructureToPtr(config, input, true);

        APIErr returnVal = (APIErr)m_wrapper.Ioctl(channelId, (int)Ioctl.SET_CONFIG, input, output);

        return returnVal;
    }
}

在意識到基本想法的巨大監督之前,我什至無法使C#高興起來,要么我的語法錯誤並且代碼無法編譯,要么會編譯但給出運行時錯誤(甚至都不會調用外部dll) )

這些問題在我身后。 該代碼現在可以正常編譯,並且可以在沒有運行時錯誤的情況下執行。 另外,我正在使用的dll具有日志記錄功能,因此我可以看到實際上正在調用正確的函數。 我什至正確地傳遞了一些數據。 該函數正在正確讀取NumOfParams變量,但是結構數組似乎是垃圾數據。

我在這里閱讀了一篇非常有幫助的文章: http : //limbioliong.wordpress.com/2012/02/28/marshaling-a-safearray-of-managed-structures-by-pinvoke-part-1/

而且我一直在閱讀MSDN,但到目前為止,我還沒有發現使這件事起作用的神奇的代碼組合,因此我要花更多時間尋求幫助。

我很確定我的問題是我沒有正確設置IntPtr變量,並且它們沒有指向內存中的正確區域。

我嘗試了不安全代碼和安全代碼的各種組合。 另外,我知道我現在還沒有顯式釋放內存,因此對此的指針也將有所幫助。 在我的研究中,這里有一些可能有用的想法,但我似乎無法使它們正確無誤

[MarshalAs(UnmanagedType.LPWStr)]

[MarshalAs(UnmanagedType.ByValArray, SizeConst=...)]

[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct, SizeConst=100)]

最后一個問題:我假設既然c ++聲明是無符號的,那么UInt32是C#中正確的類型嗎?

代碼段中的SConfig_List構造函數有很多錯誤。 最大的問題是,它為陣列分配了內存,但是完全忘記了復制結構。 因此,本機代碼可以正常使用指針,但可以查看未初始化的內存。 您可以這樣解決:

    public SConfig_List(SConfig[] config) {
        this.NumOfParams = config.Length;
        int size = Marshal.SizeOf(config[0]);
        IntPtr mem = this.configPtr = Marshal.AllocHGlobal(size * config.Length);
        for (int ix = 0; ix < config.Length; ++ix) {
            Marshal.StructureToPtr(config[ix], mem, false);
            mem = new IntPtr((long)mem + size);
        }
    }

請確保不要忘記在調用完成后再次調用Marshal.FreeHGlobal(),否則會浪費內存。

避免封送SConfig_List的最簡單方法是為C函數提供更好的聲明:

[DllImport(...)]
private static extern ApiErr PassThruIoctl(
    int channelID, 
    uint ioctlID,
    ref SConfig_List input,
    IntPtr output);

這使得像樣的包裝方法看起來像這樣:

public APIErr SetConfig(int channelId, SConfig[] config) {
    var list = new SConfig_List(config);
    var retval = PassThruIoctl(channelId, Ioctl.SET_CONFIG, ref list, IntPtr.Zero);
    Marshal.FreeHGlobal(list.configPtr);
    return retval;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM