[英]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.