.NET Compact FrameworkのP/Invoke

WZero3 でアプリを書いていて、接続の制御をしようとして適当に書いていたら、動かない。ちょっと調べてみたら、.NET Compact Framework は .NET Framework のサブセットなので完全な .NET Framework の方法とはやや異なる。ということで、注意が必要。

Microsoft .NET Compact Framework の P/Invoke とマーシャリング入門

Microsoft .NET Compact Framework での高度な P/Invoke

 
ぐちゃぐちゃ書いてあって、わかりにくいので、簡単にポイントだけ整理する。
.NET Compact Framework の相違点の概要
    • SetLastError を true にすることを忘れないように。[DllImport("abc.dll", SetLastError=true)]
    • すべてが Unicode
    • Winapi(既定のCdecl) のみをサポート
    • .NET Compact FrameworkのP/Invokeは、 コールバックをサポートしない
    • EntryPointNotFoundException, ExecutionEngineExceptionのかわりに、 MissingMethodExceptionとNotSupportedExceptionが上がる
    • Formでは、ウィンドウハンドル (hwnd)、DefWndProc メソッドがサポートされない。MessageWindow、Messageクラスを使用して、他のウィンドウにメッセージを送信できる。サンプル コード… smartdevices.microsoftdev.com 。
    • 複合オブジェクト (参照型) をマーシャリングできないことがある。特に、構造体の中にstring配列があるような場合は注意。対応法方法は複数あり。

構造体内の文字列のマーシャリング

構造体内またはクラス内の文字列ポインタを正しくマーシャリングできない。対応方法は次の3つがある。
      • サンク層での呼び出し
      • unsafe ブロックの使用
      • 文字列ポインタを処理するカスタム クラスの作成

構造体内の固定長文字列のマーシャリング

System.Char の配列が実行時に配列への 4 バイト ポインタとしてマーシャリングされるので、構造体内の固定長文字列のマーシャリングは単純には動作しない。対応方法は2つ。
    • 正確な合計サイズのバイト配列を作成した後、構造体の各フィールドをバイト配列にコピーしたり、バイト配列からコピーする。 →複雑。
    • カスタム マーシャリングを組み合わせる方法。

——————

class Memory のC#版
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace Uchukamen.WZero3
{
    class Memory
    {
        [DllImport("coredll.dll", SetLastError = true)]
        private static extern IntPtr LocalAlloc(int uFlags, int uByte);
        [DllImport("coredll.dll", SetLastError = true)]
        private static extern IntPtr LocalFree(IntPtr hMem);
        [DllImport("coredll.dll", SetLastError = true)]
        private static extern IntPtr LocalReAlloc(IntPtr hMem, int uBytes, int fuFlags);
        private const int LMEM_FIXED = 0;
        private const int LMEM_MOVEABLE = 2;
        private const int LMEM_ZEROINIT = 0x40;
        private const int LPTR = LMEM_FIXED | LMEM_ZEROINIT;
        // LocalAlloc を使用して、メモリ ブロックを割り当てます。
        public static IntPtr AllocHLocal(int cb)
        {
            return LocalAlloc(LPTR, cb);
        }
        // AllocHLocal で割り当てられたメモリを解放します。
        public static void FreeHLocal(IntPtr hlocal)
        {
            if (!hlocal.Equals(IntPtr.Zero))
            {
                if (!IntPtr.Zero.Equals(LocalFree(hlocal)))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
                hlocal = IntPtr.Zero;
            }
        }
        // 以前に AllocHLocal で割り当てられたメモリ ブロックのサイズを変更します。
        public static IntPtr ReAllocHLocal(IntPtr pv, int cb)
        {
            IntPtr newMem = LocalReAlloc(pv, cb, LMEM_MOVEABLE);
            if (newMem.Equals(IntPtr.Zero))
            {
                throw new OutOfMemoryException();
            }
            return newMem;
        }
        // マネージ文字列の内容をアンマネージ メモリにコピーします。
        public static IntPtr StringToHLocalUni(string s)
        {
            if (s == null)
                return IntPtr.Zero;
            else
            {
                int nc = s.Length;
                int len = 2 * (1 + nc);
                IntPtr hLocal = AllocHLocal(len);
                if (hLocal.Equals(IntPtr.Zero))
                    throw new OutOfMemoryException();
                else
                {
                    Marshal.Copy(s.ToCharArray(), 0, hLocal, s.Length);
                    return hLocal;
                }
            }
        }
    }
}
 

「.NET Compact FrameworkのP/Invoke」への0件のフィードバック

  1. 紹介されている参照先は、2003年当時のものですね。
    MSDNの記事ではあっても、ちょっと内容が古いかも知れません。
     
    http://msdn2.microsoft.com/ja-jp/library/h1ek3akf(VS.80).aspx
    まだ試していないんで断言できませんが上記を読む限り、.NET Compact Frameworkでも2.0以降なら
    デリゲートをコールバック関数ポインタとして渡せそうに思えるのですが…。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です