恐れ入ります。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)一部抜粋
C#
1 int iRet = 0; 2 3 // 4 //Make FelicaObject 5 // 6 using(Felica f = new Felica()) 7 { 8 // 9 //Felica Pooling 10 // 11 iRet = f.Poolling(SYS_CD) //システムコード(既定値)指定 12 13 switch(iRet) 14 { 15 case 0: //カードありの為、カード読み込み 16 byte[] bFelicaBlk = new byte[16]; 17 bFelicaBlk = f.ReadWithoutEncryption(SERVICE_CD,BLK_ADDRESS); //サービスコード、ブロックアドレス(既定値) 18 break; 19 case 1: //カードなしの為、処理なし 20 break; 21 } 22 } 23
■C#側(ラッパ)
C#
1using System; 2using System.Collections.Generic; 3using System.Text; 4using System.Runtime.InteropServices; 5 6namespace FelicaLib 7{ 8 public class Felica : IDisposable 9 { 10 //⇒★★★オリジナルの下記DllImportのコードに(CallingConvention = CallingConvention.Cdecl))を追加しました!! 11 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 12 private extern static IntPtr pasori_open(String dummy); 13 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 14 private extern static void pasori_close(IntPtr p); 15 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 16 private extern static int pasori_init(IntPtr p); 17 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 18 private extern static IntPtr felica_polling(IntPtr p, ushort systemcode, byte rfu, byte time_slot); 19 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 20 private extern static void felica_free(IntPtr f); 21 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 22 private extern static void felica_getidm(IntPtr f, byte[] data); 23 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 24 private extern static void felica_getpmm(IntPtr f, byte[] data); 25 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 26 private extern static int felica_read_without_encryption02(IntPtr f, int servicecode, int mode, byte addr, byte[] data); 27 28 private IntPtr pasorip = IntPtr.Zero; 29 private IntPtr felicap = IntPtr.Zero; 30 31 public Felica() 32 { 33 pasorip = pasori_open(null); 34 if (pasorip == IntPtr.Zero) 35 { 36 throw new Exception("felicalib.dll を開けません"); 37 } 38 if (pasori_init(pasorip) != 0) 39 { 40 throw new Exception("PaSoRi に接続できません"); 41 } 42 } 43 44 public void Dispose() 45 { 46 if (pasorip != IntPtr.Zero) 47 { 48 felica_free(felicap); //⇒★★★オリジナルのコードに追加しました! 49 felicap = IntPtr.Zero; //⇒★★★オリジナルのコードに追加しました! 50 pasori_close(pasorip); 51 pasorip = IntPtr.Zero; 52 GC.SuppressFinalize(this); //⇒★★★オリジナルのコードに追加しました! 53 } 54 } 55 56 ~Felica() 57 { 58 Dispose(); 59 } 60 61 public void Polling(int systemcode) 62 { 63 felica_free(felicap); 64 65 felicap = felica_polling(pasorip, (ushort)systemcode, 0, 0); 66 if (felicap == IntPtr.Zero) 67 { 68 return 1; //⇒★★★オリジナルのコードに追加しました!(カードがない場合はreturn 1) 69 } 70 return 0; //⇒★★★オリジナルのコードに追加しました!(カードがない場合はreturn 0) 71 } 72 73 public byte[] ReadWithoutEncryption(int servicecode, int addr) 74 { 75 if (felicap == IntPtr.Zero) 76 { 77 throw new Exception("no polling executed."); 78 } 79 80 byte[] data = new byte[16]; 81 int ret = felica_read_without_encryption02(felicap, servicecode, 0, (byte)addr, data); 82 if (ret != 0) 83 { 84 return null; 85 } 86 return data; 87 } 88 } 89} 90
C++側(dll)(内容はオリジナルより変更していません)※コードが長いので使用部分のみを転記
C++
1#include "felicalib.h" 2#include "felicaint.h" 3 4#include <tchar.h> 5#include <shlobj.h> 6#include <stdio.h> 7 8/** 9 @brief PaSoRi をオープンする 10 @param[in] dummy ダミー (libpasori との互換性のため) 11 @retval pasori ハンドル 12 13 felica.dll をロード、初期化する 14*/ 15pasori *pasori_open(char *dummy) 16{ 17 pasori *p; 18 TCHAR cp[_MAX_PATH], dllpath[_MAX_PATH]; 19 20 /* get felica.dll path */ 21 SHGetSpecialFolderPath(NULL, cp, CSIDL_PROGRAM_FILES_COMMON, FALSE); 22 _stprintf(dllpath, _T("%s\Sony Shared\FeliCaLibrary\felica.dll"), cp); 23 24 /* open felica.dll */ 25 p = (pasori *)malloc(sizeof(pasori)); 26 p->hInstDLL = LoadLibrary(dllpath); 27 if (p->hInstDLL == NULL) { 28 free(p); 29 return NULL; 30 } 31 32 /* resolve function pointers */ 33#define resolve_entry(f) p->f = (f ## _t)GetProcAddress(p->hInstDLL, #f) 34 resolve_entry(initialize_library); 35 resolve_entry(dispose_library); 36 resolve_entry(open_reader_writer_auto); 37 resolve_entry(close_reader_writer); 38 resolve_entry(polling_and_get_card_information); 39 resolve_entry(polling_and_request_system_code); 40 resolve_entry(polling_and_search_service_code); 41 resolve_entry(read_block_without_encryption); 42 resolve_entry(write_block_without_encryption); 43 44 if (!p->initialize_library()) { 45 free(p); 46 return NULL; 47 } 48 return p; 49} 50 51/** 52 @brief PaSoRi ハンドルをクローズする 53 @param[in] p pasoriハンドル 54 55 felica.dll を解放する 56*/ 57void pasori_close(pasori *p) 58{ 59 p->dispose_library(); 60 FreeLibrary(p->hInstDLL); 61} 62 63/** 64 @brief PaSoRi を初期化する 65 @param[in] p pasoriハンドル 66 @return エラーコード 67*/ 68int pasori_init(pasori *p) 69{ 70 if (!p->open_reader_writer_auto()) { 71 return -1; 72 } 73 return 0; 74} 75 76static felica *alloc_felica(pasori *p, uint16 systemcode) 77{ 78 felica *f = (felica *)malloc(sizeof(felica)); 79 memset(f, 0, sizeof(felica)); 80 f->p = p; 81 f->systemcode = H2NS(systemcode); 82 83 return f; 84} 85 86/** 87 @brief FeliCa をポーリングする 88 @param[in] p pasoriハンドル 89 @param[in] systemcode システムコード 90 @param[in] RFU RFU (使用しない) 91 @param[in] timeslot タイムスロット 92 @return felicaハンドル 93*/ 94felica *felica_polling(pasori *p, uint16 systemcode, uint8 RFU, uint8 timeslot) 95{ 96 felica *f; 97 POLLING polling; 98 uint16 sc; 99 uint8 number_of_cards = 0; 100 CARD_INFO card_info; 101 102 f = alloc_felica(p, systemcode); 103 104 sc = H2NS(systemcode); 105 polling.system_code = (uint8 *)≻ 106 polling.time_slot = timeslot; 107 108 card_info.card_idm = f->IDm; 109 card_info.card_pmm = f->PMm; 110 111 if (!p->polling_and_get_card_information(&polling, &number_of_cards, &card_info) || 112 number_of_cards == 0) { 113 free(f); 114 return NULL; 115 } 116 117 return f; 118} 119 120/** 121 @brief 暗号化されていないブロックを読み込む 122 @param[in] f felicaハンドル 123 @param[in] servicecode サービスコード 124 @param[in] mode モード(使用しない) 125 @param[in] addr ブロック番号 126 @param[out] data データ(16バイト) 127 @return エラーコード 128 129 サービスコード、ブロック番号を指定してブロックを読み込む。 130 システムコードは felica_polling で指定したものが使用される。 131*/ 132int felica_read_without_encryption02(felica *f, int servicecode, int mode, uint8 addr, uint8 *data) 133{ 134 INSTR_READ_BLOCK irb; 135 OUTSTR_READ_BLOCK orb; 136 137 uint8 service_code_list[2]; 138 uint8 block_list[2]; 139 uint8 status_flag1, status_flag2; 140 uint8 result_number_of_blocks = 0; 141 142 service_code_list[0] = servicecode & 0xff; 143 service_code_list[1] = servicecode >> 8; 144 block_list[0] = 0x80; 145 block_list[1] = addr; 146 147 irb.card_idm = f->IDm; 148 irb.number_of_services = 1; 149 irb.service_code_list = service_code_list; 150 irb.number_of_blocks = 1; 151 irb.block_list = block_list; 152 153 orb.status_flag_1 = &status_flag1; 154 orb.status_flag_2 = &status_flag2; 155 orb.result_number_of_blocks = &result_number_of_blocks; 156 orb.block_data = data; 157 158 if (!f->p->read_block_without_encryption(&irb, &orb)) { 159 return -1; 160 } 161 if (status_flag1 != 0) { 162 return -1; 163 } 164 return 0; 165} 166 167/** 168 @brief felica ハンドル解放 169 @param[in] f felica ハンドル 170*/ 171void felica_free(felica *f) 172{ 173 free(f); 174} 175
メモリリークの疑いがあったため、C#側(ラッパ)のDISPOSEに一部処理を追加しました。
今回の現象がコード上メモリリークの疑いがない場合は別の要因があるとの想定で
調査しようと思います。
初心者の質問内容で大変恐縮ですが、宜しくお願い致します。
回答1件
あなたの回答
tips
プレビュー