我試圖從Windows的SetupAPI到C#的編號SetupGetInfInformation函數。從C++到C編組SetupGetInfInformation#
我定義必要的(編組)的結構如下:
internal const uint INFINFO_INF_NAME_IS_ABSOLUTE = 2;
[StructLayout(LayoutKind.Sequential)]
internal struct SP_INF_INFORMATION
{
public uint InfStyle;
public uint InfCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] VersionData;
}
[DllImport("setupapi.dll", SetLastError = true)]
internal static extern bool SetupGetInfInformation(
[In] string InfSpec,
[In] uint SearchControl,
//[In, Out] ref SP_INF_INFORMATION ReturnBuffer,
[In, Out] ref IntPtr ReturnBuffer,
[In] uint ReturnBufferSize,
[In, Out] ref uint RequiredSize
);
然後我嘗試利用該功能,首先通過使0爲ReturnBufferSize參數,以請求所需的大小的緩衝區:
IntPtr ip = new IntPtr();
bool result = SetupAPI.SetupGetInfInformation(
@"D:\TestDriverFile\intcoed.inf",
SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, // 2
ref ip,
0,
ref i
);
這個成功的作品,始終返回這個特定的INF,所以我相信這麼多工作正常。
但是,下一步(正確分配緩衝區並第二次調用該函數以填充請求的數據的緩衝區)是我遇到困難的地方。
目前,我想這(按照以上):
ip = Marshal.AllocHGlobal((int)i);
result = SetupAPI.SetupGetInfInformation(
@"D:\TestDriverFile\intcoed.inf",
SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE,
ref ip,
i,
ref i
);
這始終崩潰vshost.exe與「vshost32.exe已停止工作」的對話,這讓我調試或關閉程序,並沒有其他有用的信息。
我也試圖改變編組SetupGetInfInformation函數簽名,以反映SP_INF_INFORMATION(而不是IntPtr的,看到上面的註釋掉的行),並在這樣做仍然可以一致稱SetupGetInfInformation在第一時間和接收。然後我試圖在緩衝器分配足夠的空間爲它,並通過在第二呼叫,如下所示:
SetupAPI.SP_INF_INFORMATION buf = new SetupAPI.SP_INF_INFORMATION();
// Make the first call, passing in ref buf, receive 422 as a response.
buf.VersionData = new byte[i];
result = SetupAPI.SetupGetInfInformation(
@"D:\TestDriverFile\intcoed.inf",
SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE,
ref buf,
i,
ref i
);
這也崩潰vshost.exe以相同的方式如上述。
對我來說看起來很明顯,我沒有爲第二次調用分配適當的緩衝區,但我可能會錯過其他所需的項目。
有人會指出我在正確的方向嗎?在StackOverflow中,我還沒有在這個特定的函數上找到任何幫助,尋找正確編組可變大小數組並且使用Marshal來分配/移位內存有幫助(和教育),但是沒有讓我過去問題。
編輯:
感謝戴夫Cluderay和西蒙Mourier。我已經接受了西蒙的溶液,作爲答案,但希望提供我完成的代碼來顯示(完全)如何封送SetupGetInfInformation並釋放非託管內存:
[StructLayout(LayoutKind.Sequential)]
public struct SP_INF_INFORMATION
{
public int InfStyle;
public int InfCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public byte[] VersionData;
}
[DllImport("setupapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool SetupGetInfInformation(
string InfSpec,
int SearchControl,
IntPtr ReturnBuffer,
int ReturnBufferSize,
ref int RequiredSize
);
public static void SetupGetInfInformation_NET(
string infPath,
ref SP_INF_INFORMATION infInfo
)
{
infInfo = new SP_INF_INFORMATION();
int size = 0;
IntPtr ip = new IntPtr();
try
{
if (!SetupAPI.SetupGetInfInformation(infPath, SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, IntPtr.Zero, 0, ref size))
throw new Exception("Error calling SetupGetInfInformation() for required buffer size.", new Win32Exception(Marshal.GetLastWin32Error()));
if (size == 0)
return;
ip = Marshal.AllocHGlobal(size);
if (!SetupAPI.SetupGetInfInformation(infPath, SetupAPI.INFINFO_INF_NAME_IS_ABSOLUTE, ip, size, ref size))
throw new Exception("Error calling SetupGetInfInformation() to retrieve INF information.", new Win32Exception(Marshal.GetLastWin32Error()));
infInfo.InfStyle = Marshal.ReadInt32(ip, 0); // The first 4-byte int is for InfStyle.
infInfo.InfCount = Marshal.ReadInt32(ip, 4); // The second 4-byte int is for InfCount.
// Marshal the data from the unmanaged buffer to a managed buffer.
byte[] buf = new byte[size];
Marshal.Copy(ip, buf, 0, size);
// Initialize VersionData to be large enough to hold the VersionData from the managed buffer. We remove 8 bytes (4 for InfStyle, 4 for InfCount.)
infInfo.VersionData = new byte[size - 8];
// Copy the VersionData from the managed buffer into infInfo.VersionData, offsetting 8 bytes for InfStyle and InfCount.
Array.Copy(buf, 8, infInfo.VersionData, 0, size - 8);
}
finally
{
Marshal.FreeHGlobal(ip);
}
}
不熟悉這個API下去,但我想改變'[輸入,輸出]裁判的IntPtr ReturnBuffer'到'[輸入,輸出] IntPtr的可能ReturnBuffer'幫助(即放棄'ref')。 –