質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.81%

Felicaの連続読み込みでメモリリークの疑い?

受付中

回答 0

投稿

  • 評価
  • クリップ 2
  • VIEW 130

score 0

恐れ入ります。C#,C++の初心者です。

Felica(PASORI)でカードの読み込みを行いたいのですが、
基本的に24時間の連続稼働でカードをかざした時にカード情報を読み込む
カードがかざされていない場合は処理なし、ですがアプリ自体は稼働し続けると
いった機能を C# で実現しようとしています。(3秒に1回にアプリを稼働させています)
ただ、どうもメモリリーク?を起こしているかもしれません。
1週間ほど連続稼働させましたが1日に1.5メガほどメモリが増加し続けています。
(windowsのパフォーマンスモニタのPrivateByteに該当のアプリを監視して確認しました)

Felicaの読み込みにはBSDライセンスの「felicalib.dll」をそのまま使用してみました。
該当のdllはC++で作成されており.NET非対応アセンブリの為、
C#側のプロジェクトの参照に追加せず、
同一ディレクトリに格納し、C#側のラッパより直接アクセスしております。
(C#のラッパもdllと一緒に配布されておりほぼそのまま使用しております)

メモリリークの疑い?があったので、DISPOSEの一部を追加してみるなどしましたが、
完全な改善には至らず、そもそもリークを起こすような記述であるのか判断がつきませんでした。

そこで、、そもそもメモリリークを起こすような可能性のあるコードであるのかどうか
御教授頂ければ幸いです。
(私自身のコードのまずい、あるいは24時間連続稼働させるような使い方をするなら別の考慮が必要等なんでも結構です!)

環境は.NetFramework4.6で稼働させております。

■C#側(Main)一部抜粋

    int iRet = 0;

    //
    //Make FelicaObject
    //
    using(Felica f = new Felica())
    {
      //
      //Felica Pooling
      //
      iRet = f.Poolling(SYS_CD)  //システムコード(既定値)指定

      switch(iRet)
      {
        case 0: //カードありの為、カード読み込み
          byte[] bFelicaBlk = new byte[16];
          bFelicaBlk = f.ReadWithoutEncryption(SERVICE_CD,BLK_ADDRESS);  //サービスコード、ブロックアドレス(既定値)
          break;
        case 1:  //カードなしの為、処理なし
          break;
      }
    }

■C#側(ラッパ)

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace FelicaLib
{
    public class Felica : IDisposable
    {
        //⇒★★★オリジナルの下記DllImportのコードに(CallingConvention = CallingConvention.Cdecl))を追加しました!!
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static IntPtr pasori_open(String dummy);
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static void pasori_close(IntPtr p);
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static int pasori_init(IntPtr p);
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static IntPtr felica_polling(IntPtr p, ushort systemcode, byte rfu, byte time_slot);
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static void felica_free(IntPtr f);
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static void felica_getidm(IntPtr f, byte[] data);
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static void felica_getpmm(IntPtr f, byte[] data);
        [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)]
        private extern static int felica_read_without_encryption02(IntPtr f, int servicecode, int mode, byte addr, byte[] data);

        private IntPtr pasorip = IntPtr.Zero;
        private IntPtr felicap = IntPtr.Zero;

        public Felica()
        {
            pasorip = pasori_open(null);
            if (pasorip == IntPtr.Zero)
            {
                throw new Exception("felicalib.dll を開けません");
            }
            if (pasori_init(pasorip) != 0)
            {
                throw new Exception("PaSoRi に接続できません");
            }
        }

        public void Dispose()
        {
            if (pasorip != IntPtr.Zero)
            {
                felica_free(felicap);               //⇒★★★オリジナルのコードに追加しました!
                felicap = IntPtr.Zero;              //⇒★★★オリジナルのコードに追加しました!
                pasori_close(pasorip);
                pasorip = IntPtr.Zero;
                GC.SuppressFinalize(this);          //⇒★★★オリジナルのコードに追加しました!
            }
        }

        ~Felica()
        {
            Dispose();
        }

        public void Polling(int systemcode)
        {
            felica_free(felicap);

            felicap = felica_polling(pasorip, (ushort)systemcode, 0, 0);
            if (felicap == IntPtr.Zero)
            {
                return 1;                           //⇒★★★オリジナルのコードに追加しました!(カードがない場合はreturn 1)
            }
            return 0;                               //⇒★★★オリジナルのコードに追加しました!(カードがない場合はreturn 0)
        }

        public byte[] ReadWithoutEncryption(int servicecode, int addr)
        {
            if (felicap == IntPtr.Zero)
            {
                throw new Exception("no polling executed.");
            }

            byte[] data = new byte[16];
            int ret = felica_read_without_encryption02(felicap, servicecode, 0, (byte)addr, data);
            if (ret != 0)
            {
                return null;
            }
            return data;
        }
    }
}

C++側(dll)(内容はオリジナルより変更していません)※コードが長いので使用部分のみを転記

#include "felicalib.h"
#include "felicaint.h"

#include <tchar.h>
#include <shlobj.h>
#include <stdio.h>

/**
   @brief PaSoRi をオープンする
   @param[in] dummy ダミー (libpasori との互換性のため)
   @retval pasori ハンドル

   felica.dll をロード、初期化する
*/
pasori *pasori_open(char *dummy)
{
    pasori *p;
    TCHAR cp[_MAX_PATH], dllpath[_MAX_PATH];

    /* get felica.dll path */
    SHGetSpecialFolderPath(NULL, cp, CSIDL_PROGRAM_FILES_COMMON, FALSE);
    _stprintf(dllpath, _T("%s\\Sony Shared\\FeliCaLibrary\\felica.dll"), cp);

    /* open felica.dll */
    p = (pasori *)malloc(sizeof(pasori));
    p->hInstDLL = LoadLibrary(dllpath);
    if (p->hInstDLL == NULL) {
        free(p);
        return NULL;
    }

    /* resolve function pointers */
#define resolve_entry(f) p->f = (f ## _t)GetProcAddress(p->hInstDLL, #f)
    resolve_entry(initialize_library);
    resolve_entry(dispose_library);
    resolve_entry(open_reader_writer_auto);
    resolve_entry(close_reader_writer);
    resolve_entry(polling_and_get_card_information);
    resolve_entry(polling_and_request_system_code);
    resolve_entry(polling_and_search_service_code);
    resolve_entry(read_block_without_encryption);
    resolve_entry(write_block_without_encryption);

    if (!p->initialize_library()) {
        free(p);
        return NULL;
    }
    return p;
}

/**
   @brief PaSoRi ハンドルをクローズする
   @param[in] p pasoriハンドル

   felica.dll を解放する
*/
void pasori_close(pasori *p)
{
    p->dispose_library();
    FreeLibrary(p->hInstDLL);
}

/**
   @brief PaSoRi を初期化する
   @param[in] p pasoriハンドル
   @return エラーコード
*/
int pasori_init(pasori *p)
{
    if (!p->open_reader_writer_auto()) {
        return -1;
    }
    return 0;
}

static felica *alloc_felica(pasori *p, uint16 systemcode)
{
    felica *f = (felica *)malloc(sizeof(felica));
    memset(f, 0, sizeof(felica));
    f->p = p;
    f->systemcode = H2NS(systemcode);

    return f;
}

/**
   @brief FeliCa をポーリングする
   @param[in] p pasoriハンドル
   @param[in] systemcode システムコード
   @param[in] RFU RFU (使用しない)
   @param[in] timeslot タイムスロット
   @return felicaハンドル
*/
felica *felica_polling(pasori *p, uint16 systemcode, uint8 RFU, uint8 timeslot)
{
    felica *f;
    POLLING polling;
    uint16 sc;
    uint8 number_of_cards = 0;
    CARD_INFO card_info;

    f = alloc_felica(p, systemcode);

    sc = H2NS(systemcode);
    polling.system_code = (uint8 *)&sc;
    polling.time_slot = timeslot;

    card_info.card_idm = f->IDm;
    card_info.card_pmm = f->PMm;

    if (!p->polling_and_get_card_information(&polling, &number_of_cards, &card_info) ||
        number_of_cards == 0) {
        free(f);
        return NULL;
    }

    return f;
}

/**
   @brief 暗号化されていないブロックを読み込む
   @param[in] f felicaハンドル
   @param[in] servicecode サービスコード
   @param[in] mode モード(使用しない)
   @param[in] addr ブロック番号
   @param[out] data データ(16バイト)
   @return エラーコード

   サービスコード、ブロック番号を指定してブロックを読み込む。
   システムコードは felica_polling で指定したものが使用される。
*/
int felica_read_without_encryption02(felica *f, int servicecode, int mode, uint8 addr, uint8 *data)
{
    INSTR_READ_BLOCK irb;
    OUTSTR_READ_BLOCK orb;

    uint8 service_code_list[2];
    uint8 block_list[2];
    uint8 status_flag1, status_flag2;
    uint8 result_number_of_blocks = 0;

    service_code_list[0] = servicecode & 0xff;
    service_code_list[1] = servicecode >> 8;
    block_list[0] = 0x80;
    block_list[1] = addr;

    irb.card_idm = f->IDm;
    irb.number_of_services = 1;
    irb.service_code_list = service_code_list;
    irb.number_of_blocks = 1;
    irb.block_list = block_list;

    orb.status_flag_1 = &status_flag1;
    orb.status_flag_2 = &status_flag2;
    orb.result_number_of_blocks = &result_number_of_blocks;
    orb.block_data = data;

    if (!f->p->read_block_without_encryption(&irb, &orb)) {
        return -1;
    }
    if (status_flag1 != 0) {
        return -1;
    }
    return 0;
}

/**
   @brief felica ハンドル解放
   @param[in] f felica ハンドル
*/
void felica_free(felica *f)
{
    free(f);
}

メモリリークの疑いがあったため、C#側(ラッパ)のDISPOSEに一部処理を追加しました。
今回の現象がコード上メモリリークの疑いがない場合は別の要因があるとの想定で
調査しようと思います。

初心者の質問内容で大変恐縮ですが、宜しくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正の依頼

  • radian

    2021/07/22 20:15

    https://qiita.com/mindwood/items/103fc0fb52bca7773e47
    ライブラリに特に拘りがないなら、他の奴も試してみては

    キャンセル

  • yotuika

    2021/07/23 17:40

    radianさん
    ご丁寧に有難うございます!
    コール数をカウントしたところ1日に3万回弱、1週間で20万回ほど稼働させてみた結果です。
    ライブラリに特にこだわりはありませんので、もう少し調べて改善しないようであれば、
    試してみます!有難うございました

    キャンセル

  • yotuika

    2021/07/23 17:43 編集

    dodox86さん
    確かにほんとですね!
    dll内部の処理であるので、dllを再作成してみようと思います!!
    なんかこれが原因のような気がしてきました。
    有難うございます!!

    キャンセル

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.81%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る