[英]passing float pointer as IntPtr (pinvoke)
好的,我有一個DLL函數聲明為:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref IntPtr param_value);
該函數采用許多不同的參數,即在C ++中這就是您將如何使用它。
int i=2;
setParam("width", &i);
float k=2.5f;
setParam("factor", &f);
所以我試圖聲明一個C#函數來調用此DLL api,我有一個工作於整數大小寫的指針:
public static void setWidth(int width)
{
IntPtr w = new IntPtr(width);
setParam("width", ref w);
}
但是我無法弄清楚第二個方法,在該方法中我將指針傳遞給IntPtr作為浮點數。 有任何想法嗎?
public static void setFactor(float f)
{
IntPtr w = new IntPtr(f); // WHAT GOES HERE??
setParam("factor", ref w);
}
除非有太多的組合,否則我想說最好的方法是為各種參數類型簡單地具有多個DllImport
。 例如:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParamInt32(string param_name, ref int param_value);
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParamSingle(string param_name, ref float param_value);
然后您可以正確地稱呼他們為
var intVal = 42;
setParamInt32("param", ref intVal);
var floatVal = 42.0f;
setParamSingle("param", ref floatVal);
在這兩種情況下,使用ref IntPtr
都是錯誤的-根本ref IntPtr
的唯一原因是在32位應用程序中, IntPtr
在內部是32位整數。 但是,它應該是一個指針 。 正確使用將是這樣的:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, IntPtr param_value);
請注意, ref
不存在IntPtr
已經是間接ref
。
要調用此方法,您需要分配一些內存,並獲得指向該內存的指針-或使用GCHandle
直接引用托管對象:
var intValue = 42;
var handle = GCHandle.Alloc(intValue, GCHandleType.Pinned);
setParam("param", handle.AddrOfPinnedObject());
當然,請確保正確處置托管手柄-固定手柄對於GC來說是一個巨大的痛苦。
手動將數據復制到非托管內存並返回也並不困難:
var ptr = Marshal.AllocCoTaskMem(sizeof(int));
try
{
Marshal.WriteInt32(ptr, 42);
setParam("param", ptr);
// If you need to read the value back:
var result = Marshal.ReadInt32(ptr);
}
finally
{
Marshal.FreeCoTaskMem(ptr);
}
但是除非您有很好的理由不這樣做,否則我只會堅持使用自動編組。
如果您絕對必須使用IntPtr
並且不能僅傳遞float
參數,則可以使用unsafe
編碼來傳遞float
指針,因為IntPtr
在其構造函數之一中將void
指針作為參數。 但是,相應的非托管函數也必須使用void
指針。
您可以通過兩種方式處理此問題,即傳遞void*
或傳遞IntPtr*
。 我會說傳遞void*
可能更好,因為您通常會對IntPtr
進行相同的操作,只不過IntPtr
將與指針而不是函數一起傳遞。
選項1-
IntPtr
首先,您必須通過刪除錯誤的ref
來更正p / invoke聲明
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, IntPtr param_value);
您也不會通過IntPtr
作為ref
,而只是傳遞您的IntPtr
的實例
public static unsafe void setFactor(float f)
{
IntPtr w = new IntPtr(&f);
setParam("factor", w);
}
或者如果您想在體內聲明不安全
public static void setFactor(float f)
{
unsafe
{
IntPtr w = new IntPtr(&f);
setParam("factor", w);
}
}
選項2-
void*
[DllImport(mDllName, EntryPoint = "SetParam")]
public unsafe static extern bool setParam(string param_name, void* param_value);
然后您可以將其設置為
public static unsafe void setWidth(int width)
{
int* w = &width;
setParam("width", w);
}
而對於float
public static unsafe void setFactor(float f)
{
float* fptr = &f;
setParam("factor", fptr);
}
理論上,您可以為“安全”自動編組聲明不同的DllImport
條目:
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref int param_value);
[DllImport(mDllName, EntryPoint = "SetParam")]
public static extern bool setParam(string param_name, ref float param_value);
並像這樣使用它:
public static void setFactor(float f)
{
setParam("factor", ref f);
}
我已經使用void *
作為簽名為其他函數完成了此操作,並且它工作得很好, ref <value_type>
被正確地傳遞為指針。
另外, ref IntPtr
應該用於void **
(您可以將IntPtr
用於void *
,而不是ref IntPtr
)
如果您認為不安全,可以使用@Bauss解決方案
此外,使用構造函數IntPtr(int)
給出指針的位置,而不是該位置的值
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.