次のメソッドを C++ から C# に変換しようとしています
HRESULT GetDevices(
[in, out] LPWSTR *pPnPDeviceIDs,
[in, out] DWORD *pcPnPDeviceIDs
);
MSDN ドキュメントには次のように書かれています。
pPnPDeviceIDs [入力、出力]
接続されているすべてのデバイスのプラグ アンド プレイ名を保持する、呼び出し元によって割り当てられた文字列ポインターの配列。このパラメータに必要なサイズを知るには、まずこのパラメータを 0 に設定してこのメソッドを呼び出しNULL
、pcPnPDeviceIDs
次に によって取得された値に従ってバッファを割り当てますpcPnPDeviceIDs
。pcPnPDeviceIDs [in, out] 入力時、
pPnPDeviceIDs
保持できる値の数。出力では、実際に書き込まれたデバイスの数へのポインターpPnPDeviceIDs
。
これまでのところ、次の定義があります。
[PreserveSig]
int GetDevices(
[In, Out] IntPtr pPnPDeviceIDs,
[In, Out] ref uint pcPnPDeviceIDs
);
Marshal.AllocCoTaskMem
for でメモリを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() への 2 回目の呼び出しで AccessViolationException を取得します。