我正在尝试将以下方法从C ++转换为C#
HRESULT GetDevices(
[in, out] LPWSTR *pPnPDeviceIDs,
[in, out] DWORD *pcPnPDeviceIDs
);
在MSDN文档中说:
pPnPDeviceIDs [in,out]
调用者分配的字符串指针数组,包含所有连接设备的即插即用名称。要了解此参数所需的大小,请首先调用此方法,并将此参数设置为NULL
并将pcPnPDeviceIDs
设置为零,然后根据pcPnPDeviceIDs
检索的值分配缓冲区。pcPnPDeviceIDs [in,out] 输入时,
pPnPDeviceIDs
可以容纳的值的数量。在输出时,指向实际写入pPnPDeviceIDs
的设备数量的指针。
到目前为止,我有这个定义:
[PreserveSig]
int GetDevices(
[In, Out] IntPtr pPnPDeviceIDs,
[In, Out] ref uint pcPnPDeviceIDs
);
我尝试为Marshal.AllocCoTaskMem
分配一些带有pPnPDeviceIDs
的内存,但是当我尝试调用该方法时,我得到了一个AccessViolationException。
有人能告诉我转换LPWSTR *
字符串指针数组的正确方法吗?
编辑: 以下是我的定义:
[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("a1567595-4c2f-4574-a6fa-ecef917b9a40")]
public interface IPortableDeviceManager
{
[PreserveSig]
HRESULT GetDeviceDescription(
[In] string pszPnpDeviceID,
[In, Out] ref StringBuilder pDeviceDescription,
[In, Out] ref uint pcchDeviceDescription);
[PreserveSig]
HRESULT GetDeviceFriendlyName(
[In] string pszPnPDeviceID,
[In, Out] ref StringBuilder pDeviceFriendlyName,
[In, Out] ref uint pcchDeviceFriendlyName);
[PreserveSig]
HRESULT GetDeviceManufacturer(
[In] string pszPnPDeviceID,
[In, Out] ref StringBuilder pDeviceManufacturer,
[In, Out] ref uint pcchDeviceManufacturer);
[PreserveSig]
HRESULT GetDeviceProperty(
[In] string pszPnPDeviceID,
[In] string pszDevicePropertyName,
[In, Out] IntPtr pData,
[In, Out] ref uint pcbData,
[In, Out] ref uint pdwType);
[PreserveSig]
HRESULT GetDevices(
[In, Out] IntPtr pPnPDeviceIDs,
[In, Out] ref uint pcPnPDeviceIDs);
[PreserveSig]
HRESULT GetPrivateDevices(
[In, Out] IntPtr pPnPDeviceIDs,
[In, Out] ref uint pcPnPDeviceIDs);
[PreserveSig]
HRESULT RefreshDeviceList();
}
/// <summary>
/// CLSID_PortableDeviceManager
/// </summary>
[ComImport, Guid("0af10cec-2ecd-4b92-9581-34f6ae0637f3")]
public class CLSID_PortableDeviceManager { }
这是我的测试代码:
var devManager = Activator.CreateInstance(
typeof(CLSID_PortableDeviceManager)) as IPortableDeviceManager;
uint pcPnPDeviceIDs = 0;
var res1 = devManager.GetDevices(IntPtr.Zero, ref pcPnPDeviceIDs);
// check for errors in res1
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocCoTaskMem((int)(IntPtr.Size * pcPnPDeviceIDs));
var res2 = devManager.GetDevices(ptr, ref pcPnPDeviceIDs);
// check for errors in res2
IntPtr ptr2 = ptr;
for (uint i = 0; i < pcPnPDeviceIDs; i++)
{
string str = Marshal.PtrToStringUni(ptr2);
ptr2 += IntPtr.Size;
}
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(ptr);
}
}
Marshal.ReleaseComObject(devManager);
第二次调用GetDevices()
时出现AccessViolationException答案 0 :(得分:0)
试试这个:
uint pcPnPDeviceIDs = 0;
int res1 = GetDevices(IntPtr.Zero, ref pcPnPDeviceIDs);
// check for errors in res1
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocCoTaskMem((int)(IntPtr.Size * pcPnPDeviceIDs));
int res2 = GetDevices(ptr, ref pcPnPDeviceIDs);
// check for errors in res2
IntPtr ptr2 = ptr;
for (uint i = 0; i < pcPnPDeviceIDs; i++)
{
string str = Marshal.PtrToStringUni(Marshal.ReadIntPtr(ptr2));
ptr2 += IntPtr.Size;
}
}
finally
{
if (ptr != IntPtr.Zero)
{
IntPtr ptr2 = ptr;
for (uint i = 0; i < pcPnPDeviceIDs; i++)
{
Marshal.FreeCoTaskMem(Marshal.ReadIntPtr(ptr2));
ptr2 += IntPtr.Size;
}
Marshal.FreeCoTaskMem(ptr);
}
}
我使用uint
(s)用于HRESULT,因为它们必须逐位解析(对于无符号整数,它更容易)
系统内存的分配应始终受try/finally
保护。并记得释放所有字符串:-)(正如我所做的那样)
使用IntPtr[]
的变体:
GetDevices的定义(注意使用[MarshalAs(UnmanagedType.LPArray)]
:
[PreserveSig]
HRESULT GetDevices(
[In][MarshalAs(UnmanagedType.LPArray)] IntPtr[] pPnPDeviceIDs,
[In, Out] ref uint pcPnPDeviceIDs);
剩下的代码:
var devManager = Activator.CreateInstance(typeof(CLSID_PortableDeviceManager)) as IPortableDeviceManager;
uint pcPnPDeviceIDs = 0;
HRESULT res1 = devManager.GetDevices(null, ref pcPnPDeviceIDs);
// check for errors in res1
IntPtr[] ptr = null;
try
{
ptr = new IntPtr[pcPnPDeviceIDs];
HRESULT res2 = devManager.GetDevices(ptr, ref pcPnPDeviceIDs);
// check for errors in res2
for (uint i = 0; i < pcPnPDeviceIDs; i++)
{
string str = Marshal.PtrToStringUni(ptr[i]);
}
}
finally
{
if (ptr != null)
{
for (uint i = 0; i < pcPnPDeviceIDs; i++)
{
Marshal.FreeCoTaskMem(ptr[i]);
}
}
}
通过这种方式,您不必在非托管内存中分配主数组。