簡體   English   中英

將float指針作為IntPtr傳遞(pinvoke)

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

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