0

マルチバイト配列をファイルに直接書き込み/読み取りしようとしていますが、PInvoke WriteFile/ReadFileを使用するように提案されました。

基本的に、私の読み取りコードは次のようになります。

[DllImport("kernel32.dll", SetLastError = true)]
static extern unsafe int ReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead,
  IntPtr numBytesRead, System.Threading.NativeOverlapped* overlapped);

..<cut>..

byte[,,] mb = new byte[1024,1024,1024];
fixed(byte * fb = mb)
{
    FileStream fs = new FileStream(@"E:\SHARED\TEMP", FileMode.Open);
    int bytesread = 0;
    ReadFile(fs.SafeFileHandle.DangerousGetHandle(), (IntPtr)fb, Convert.ToUInt32(mb.Length), new IntPtr(bytesread), null);
    fs.Close();
}

このコードはAccessViolationExceptionをスローします。ただし、次のコードはそうではありません。

[DllImport("kernel32.dll", SetLastError = true)]
static extern unsafe int ReadFile(IntPtr handle, IntPtr bytes, uint numBytesToRead,
  ref int numBytesRead, System.Threading.NativeOverlapped* overlapped);

..<cut>..

byte[,,] mb = new byte[1024,1024,1024];
fixed(byte * fb = mb)
{
    FileStream fs = new FileStream(@"E:\SHARED\TEMP", FileMode.Open);
    int bytesread = 0;
    ReadFile(fs.SafeFileHandle.DangerousGetHandle(), (IntPtr)fb, Convert.ToUInt32(mb.Length), ref bytesread, null);
    fs.Close();
}

違いは、numBytesReadをIntPtrではなくrefintとして宣言することです。

ただし、「IntPtrをintに変換する方法」という質問に対する答えが見つかるところはどこでも、次のようになります。

int x = 0;
IntPtr ptrtox = new IntPtr(x)

だから、私は何が間違っているのですか?なぜアクセス違反なのですか?

4

4 に答える 4

0

安全でないコンテキストにいる場合は、CまたはC ++の場合と同じように、intなどのblittable型へのポインターを取得できます。あなたの場合、&bytesread。とはいえ、単純なポインタパラメータの場合は、常にrefまたはoutキーワードを使用する必要があります。

于 2012-12-13T20:39:42.717 に答える
0

簡単だ。あなたがしているこの小さな部分を見てください:

new IntPtr(bytesread)

それはあなたが思っていることをしません。あなたはそれがあなたの変数bytesreadを指す新しいポインタを作ると思います。そうではありません。これは、 bytesreadのが0のアドレスを指すポインターを作成します。アンマネージコードは、nullポインターが指すメモリに数値を読み取ろうとしますが、失敗します。

ref int他のバージョンは、マーシャラーが値の代わりにbytesreadへの実際のポインターを渡すように引数が宣言されているために機能します。

于 2012-12-13T13:41:32.527 に答える
0

アクセス違反は bytesread が管理されているためだと思います。したがって、GC がそれを移動し、渡したポインターが無効になる可能性があります。

以下は機能しますか?

int bytesread = 0;
var pin = GCHandle.Alloc(bytesread, GCHandleType.Pinned)
ReadFile(fs.SafeFileHandle.DangerousGetHandle(), (IntPtr)fb, Convert.ToUInt32(mb.Length), pin.AddrOfPinnedObject(), null);

[編集] 次の行を忘れました:

pin.Free();

[二重編集] なんてこった!スティックの端を完全に間違えました。私が言っていることは、安全なコードでヒープから管理されたデータを処理することにもっと当てはまります。

@plinth はまさに正しい、コード:

int x = 0;
IntPtr ptrtox = new IntPtr(x)

x を指すのではなく、x の値を持つポインターを作成します。元のコードでは、次を渡すだけです。

new IntPtr(&bytesread)

また

(IntPtr)(&bytesread)
于 2012-12-13T13:27:33.547 に答える