2015年3月1日日曜日

FTDIドライバをLinux/Window cross-platform 対応にする

LoadLibrary周りがWindowsAPI直接利用しているので、この部分をLinuxに対応させなくては行けない。

ということで参考サイトをみると、その部分はインターフェイスに分離してWin/Linux切り替えできるように実装する方法が乗っていたのでやり方を書いていく。

まずはインターフェイス
interface DllLoadUtils {
        IntPtr LoadLibrary(string fileName);
        void FreeLibrary(IntPtr handle);
        IntPtr GetProcAddress(IntPtr dllHandle, string name);
}

次に

Windows実装部
    public class DllLoadUtilsWindows : DllLoadUtils {
        void DllLoadUtils.FreeLibrary(IntPtr handle) {
            FreeLibrary(handle);
        }

        IntPtr DllLoadUtils.GetProcAddress(IntPtr dllHandle, string name) {
            return GetProcAddress(dllHandle, name);
        }

        IntPtr DllLoadUtils.LoadLibrary(string fileName) {
            return LoadLibrary(fileName);
        }

        [DllImport(" extern="" filename="" freelibrary="" getprocaddress="" handle="" int="" intptr="" kernel32.dll="" kernel32="" llimport="" loadlibrary="" ntptr="" pre="" private="" procedurename="" static="" string="">
Linux実装部
    internal class DllLoadUtilsLinux : DllLoadUtils {
        public IntPtr LoadLibrary(string fileName) {
            return dlopen(fileName, RTLD_NOW);
        }

        public void FreeLibrary(IntPtr handle) {
            dlclose(handle);
        }

        public IntPtr GetProcAddress(IntPtr dllHandle, string name) {
            // clear previous errors if any
            dlerror();
            var res = dlsym(dllHandle, name);
            var errPtr = dlerror();
            if (errPtr != IntPtr.Zero) {
                throw new Exception("dlsym: " + Marshal.PtrToStringAnsi(errPtr));
            }
            return res;
        }

        const int RTLD_NOW = 2;

        [DllImport("libdl.so")]
        private static extern IntPtr dlopen(String fileName, int flags);
        
        [DllImport("libdl.so")]
        private static extern IntPtr dlsym(IntPtr handle, String symbol);

        [DllImport("libdl.so")]
        private static extern int dlclose(IntPtr handle);

        [DllImport("libdl.so")]
        private static extern IntPtr dlerror();
    }
こうすることでLinuxとWindowsで処理を分離できるようになったわけだがすでに呼び出ししてる箇所の置き換えは大変なので、関数でかぶせる感じにする。
  #region LOAD_LIBRARIES
  /// 
  /// Built-in Windows API functions to allow us to dynamically load our own DLL.
  /// Will allow us to use old versions of the DLL that do not have all of these functions available.
  /// 
  //[DllImport("kernel32.dll")]
  //private static extern IntPtr LoadLibrary(string dllToLoad);
  //[DllImport("kernel32.dll")]
  //private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
  //[DllImport("kernel32.dll")]
  //private static extern bool FreeLibrary(IntPtr hModule);
  private DllLoadUtils dllLoadUtils;
  private IntPtr LoadLibrary(string dllToLoad)
  {
   return this.dllLoadUtils.LoadLibrary(dllToLoad);
  }
  private IntPtr GetProcAddress(IntPtr hModule, string procedureName)
  {
   return this.dllLoadUtils.GetProcAddress(hModule, procedureName);
  }
  private bool FreeLibrary(IntPtr hModule)
  {
   this.dllLoadUtils.FreeLibrary(hModule);
  }
  private bool IsLinux() {
   var p = (int) Environment.OSVersion.Platform;
   return (p == 4) || (p == 6) || (p == 128);
  }
  #endregion
  public FTDI()
  {
   // If FTD2XX.DLL is NOT loaded already, load it
   if (hFTD2XXDLL == IntPtr.Zero)
   {
    // Load our FTD2XX.DLL library
    if (IsLinux())
    {
     this.dllLoadUtils = new DllLoadUtilsLinux() 
     hFTD2XXDLL = LoadLibrary(@"libftd2xx.so");
    }
    else
    {
     this.dllLoadUtils = new DllLoadUtilsWindows();
     hFTD2XXDLL = LoadLibrary(@"FTD2XX.DLL");
    }
                 ...
あとはMessageBox。System.Windows.Formsを利用しているのでLinuxでは利用できない。
なのでstaticクラスなスタブ関数を用意する。
    internal static class MessageBox
    {
        public static void Show(string message)
        {
            Console.WriteLine(message);
        }
    }

試しに.netアプリを使ってみるとDLLで対応していない?APIがあって
 Rescan
 Reload
 GetComPortNumber
この3つがエラーになった。
とりあえずコメントアウトしておくこと。

この後Linux側でオープンできないとかが発生した場合は
共有ライブラリ周りがおかしいので、詳細はこっちへ
http://ivis-mynikki.blogspot.jp/2015/02/raspberry-piftdi.html


参考サイト

http://dimitry-i.blogspot.jp/2013/01/mononet-how-to-dynamically-load-native.html
コメントを投稿

Androider