Microsoft MVP성태의 닷넷 이야기
C#에서의 함수포인터 [링크 복사], [링크+제목 복사]
조회: 11170
글쓴 사람
왕초보
홈페이지
첨부 파일
 
안녕하세요? 성태님.
카메라 한대 사서 프로그래밍중인데요...ㅠㅠ~ dll에 파라미터로 정의된 함수포인터 골치아프네요.
카메라에 첨부된 샘플에는 C++로 아래와 같이 되어있는데...

__declspec(dllimport) MVSTATUS_CODES __stdcall MVStartGrab(HANDLE hCam,int (*StreamCB)(MV_IMAGE_INFO*,long nUserVal),long nUserVal);
...

int StreamCB(MV_IMAGE_INFO *pInfo,long nUserVal)
{
 ...
 return 0;
}
...
void CGCapDlg::OnBnClickedStart()
{
 ...
 MVStartGrab(m_hCam,StreamCB,(long)this);
 ...
}
흠~~ 위의 코드는 dll의 MVStartGrab함수가 카메라로부터 화상을 읽고 동기에 맞추어 읽은 화상데이터를 사용자에게 넘겨주기 위해 사용자처리루틴(int StreamCB(...))을
함수포인터로 넘겨받는 과정이죠. 일단 MVStartGrab(...)함수가 실행되면 동기에 맞춰 StreamCB가 주기적으로 호출되요.
C#에서 아래와 같이 변환했어요.^^

class Form1
{
 ...
 delegate short StreamCB(ref MV_IMAGE_INFO imageInfo, int nUserVal);
 [DllImport("MVGigE.dll")]
 public static extern MvStatus MVStartGrab(IntPtr hCam, StreamCB streamCB, int nUserVal);
 ...

 short OnStreamCB(ref MV_IMAGE_INFO info, int nUserVal)
 {
   ...
   return 0;
 }
 ...
 StreamCB sb;
 private void b_CamRun_Click(object sender, EventArgs e)
 {
   ...
   sb = OnStreamCB;
   MVStartGrab(m_hCam, sb, (int)this.Handle);
   ...
 }
}
나로선 어딘가 틀린데가 없어보이는데 OnStreamCB는 맨 첨 한번밖에 실행안되요.
한번만은 데이터랑 완전하게 받아요.
내보기엔 초기에 넘겨준 함수포인터를 잃어버리는것같은데 왜 그럴까요?
MFC에선 제대로 작동되요. 물론 MFC로 해도 되겠지만 품들여 C#에서 영상처리기반 자작했는데... 꼭 C#으로 돌리고 싶네요.
귀중한 시간 자꾸 앗아서 죄송합니다.




donaricano-btn



[최초 등록일: ]
[최종 수정일: 11/1/2014 ]


비밀번호

댓글 쓴 사람
 



2014-11-03 12시32분
이게 문제인지는 모르겠지만, StreamCB 함수 포인터의 반환값이 int 인데, C# 측에서는 short로 되어 있습니다. 그리고, MV_IMAGE_INFO 구조체를 잘 구성했는지, sizeof 같은 방법을 이용해서 C++에서의 크기와 C#에서의 크기가 동일한지 확인해 보세요. 그 외에는, 딱히 문제가 없어 보입니다.
정성태
2014-11-03 03시28분
[왕초보] C++에서 정의된 구조체는

typedef struct _IMAGE_INFO
{
    uint64_t    nTimeStamp;        
    USHORT        nBlockId;        
    UCHAR        *pImageBuffer;
    ULONG        nImageSizeAcq;    
    UCHAR        nMissingPackets;
    uint64_t    nPixelType;        
    uint32_t    nSizeX;            
    uint32_t    nSizeY;
    uint32_t    nOffsetX;        
    uint32_t    nOffsetY;
} MV_IMAGE_INFO, * pMV_IMAGE_INFO ;

이구요 C#에서 구현한 구조체는

 unsafe public struct MV_IMAGE_INFO
 {
        public ulong nTimeStamp;        
        public ushort nBlockId;        
        public byte* pImageBuffer;    
        public uint nImageSizeAcq;    
        public byte nMissingPackets;
        public ulong nPixelType;        
        public uint nSizeX;            
        public uint nSizeY;
        public uint nOffsetX;        
        public uint nOffsetY;
        public MV_IMAGE_INFO (ulong Ts, ushort BI, byte* IB, uint ISA, byte MP, ulong PT, uint SX, uint SY, uint OX, uint OY)
        {
            nTimeStamp = Ts;
            nBlockId = BI;
            pImageBuffer = IB;
            nImageSizeAcq = ISA;
            nMissingPackets = MP;
            nPixelType = PT;
            nSizeX = SX;
            nSizeY = SY;
            nOffsetX = OX;
            nOffsetY = OY;
        }
  }

예요. 함수 반환값은 int로 바꿔보았고 unsafe예약어 잘못썼나 해서 위의 구조체 리용하는곳들에 다 unsafe달아줘봤어요. C++과 C#에서 위의 구조체 크기는 48로 같게 나오고... 하지만 결과는 같애요.^^
dll로 실행권이 넘어가면 디버그도 할수 없어 속상하네요. 이상한건 첨 한번 결과를 되돌린 후 dll이 전혀 작동 안하는건 아니고, Task Manager에서 보면, 메모리 사용량은 C++과 같은데, CPU이용량은 C#쪽이 엄청 크네요(70%정도로 CPU를 아예 독점해버리죠. ㅠㅠ~~).
결국 함수 포인터 한번 참조후에 실행주소가 향방을 잃은것같은데...???^^. 그렇다고 완전 크래쉬된건 아녜요. 다른 함수들 호출하면 제대로 작동되요.
C++에서 함수포인터는 본질상 그냥 포인터이고 C#에서는 delegate로 선언된 객체가 클래스네요. delegate로 C++에 함수포인터 넘기는 메카니즘 표상이 잘 안가요. 그리고,
C++에서 전역함수와 클래스 멤버함수의 포인터 참조형식이 좀 다르죠. 근데 C#에는 전역함수나 전역변수를 선언할수 없어요. 혹시 시리얼화된 클래스 멤버함수의 주소배치가 C++과 C#에서 조금 차이나는건 아닌지...
이런 의문점들은 배열이나 string타입에서도 같죠. C++의 CString은 구조체로 선언되었고 본질상 char형의 배렬로 구성되지만 C#의 string은 강력한 탐색기능을 제공하는 클래스이고, 근데 C++과의 호환에서는 System.Runtime.InteropServices만 using하고 그냥 통한다고 하네요.(실제로 해보면 LPCSTR 에로의 string변환도 잘 안되요.)^^ 후~~으-ㅁ~~...암튼 이루루한 메카니즘 표상을 정확히 갖고 머리를 정돈해서 봐야 할것같군요...
말이 길어져서 미안합니다. 살틀한 방조에 사의를 표합니다.

[손님]
2014-11-03 08시47분
[왕초보] p.s.
혹시 구조체에 포함된 byte*포인터변수때문이 아닌지...^^
제생각에는, 제 프로젝트에서 위의 구조체리용은 C#쪽에서 메모리를 할당한것이 아니라 C++로 컴파일된 dll에서 할당한 주소공간을 단지 넘겨받는것이라서 가비지수집과는 무관한걸로 예상되는데...
unsafe예약어를 없애고 byte*타입을 IntPtr타입으로 바꾸어주어도 별다른 변화가 없어요.제가 써넣은 코드상에서 포인터관리가 잘못되였는지 테스트 하느라 OnStreamCB함수에서 아무런 처리도 하지 않고 포인터를 넘겨줘도 호출은 한번밖에 안되요.
만일 참조를 포함한 구조체참조에 문제가 있다면, 위의 경우는 피할길이 없다는 절망감이 드네요...^^
[손님]
2014-11-03 11시21분
[왕초보] 문제가 또 있네요^^.
앞에서도 말씀드렸지만 함수포인터를 받는 dll함수는 그 포인터를 받아 일단 내부에 저장하고, thread를 창조하여 포인터로 지적된 함수를 주기적으로 호출해요.
근데 dll함수를 호출할 당시 넘겨준 함수(StreamCB)의 포인터값은 이후 가비지수집기에 의해 변경될 가능성이 있어요. 결국 dll함수의 입장에서 보면, 넘겨받은 포인터가 지적하는 함수에로의 한번 접근 후, 다음번 호출시 같은 포인터로 접근하면 그 주소에는 이미 그 함수가 없어지고, 따라서 포인터조작은 향방을 잃게되죠... (어쨋든 이건 가정이고 .Net에 왕초보인 저로서는 아마 그럴거라고...ㅎㅎㅎ)
메모리를 고정하는 fixed예약어가 있기는 한데 브레이크로 제한된 구역에서만 효과를 가지니 위의 경우에는 효력이 없고 ㅠㅠ~~...
많은 API함수들이 내부에 thread를 가지고 있고 고정된 주소에로의 자료반환을 진행해요. 이러한 경우를 예상해서 C#에서도 대책이 있을거라고 생각되요.
전 왕초보라서 가정에 가정만을 되풀이하는데 선배님의 고견 기대됩니다. 선배님께 자꾸 수고로움 끼치게 됩니다.
[손님]
2014-11-03 12시48분
우선 GC에 의한 포인터 변경은 신경쓰지 않아도 됩니다. PInvoke 호출 시에 delegate에 대해서는 자동으로 pinning 시키기 때문입니다. byte * 포인터는, 말씀하신 것처럼 OnStremCB 측에서 아무런 처리도 하지 않고 포인터를 넘겼는데도 그런 현상이 있는 것이라면 무관한 것 같습니다.

정히 안되면, 그냥 C++/CLI로 해보세요. 아니면, 간단한 C#/C++ 예제를 만들어서 IMAGE_INFO를 동일하게 넘겨보고 문제 발생 지점을 축소해서 재현해 보세요.

(그나저나... 사용하시는 용어가 특이하시군요. '살틀한 방조에 사의를 표...', '이루루한 메카니즘 표상' 같은 말은 무슨 뜻인지 모르겠습니다. ^^;)
정성태

... 31  32  33  34  35  36  37  38  39  40  41  42  [43]  44  45  ...
NoWriterDateCnt.TitleFile(s)
1156정환나라6/21/20135900In-Process Side-By-Side 동작 원리는 어떻게 되는걸까요? [3]
1155MD워시퍼6/20/20135449안녕하세요. 한가지 궁금한 것이 있어서 문의를 드립니다. [1]
1154it 대학생6/13/20138428무선 패킷 스니핑 프로그램 [1]
1153이상헌6/8/20135544죄송합니다;; 또다시 의문점이 생겨 질문드립니다. [1]
1152이상헌6/8/20135006넷두이노 플러스 펌웨어 다운로드가 안되네요;;; [1]
1151황기동6/5/20136351무선 패킷 전송관련 질문 [1]
1147이진권6/2/20135861c# 윈폼 facebook과 연동2 [1]
1144황은영5/29/201310200원격지 서버에서 COM+ 호출이 되지 않습니다. [7]
1143김선희5/28/201310843윈도우 8, 익스플로러 10, 64bit->32bit [1]
1142백지훈5/15/20136023음성인식 TTS 관련해서 질문드립니다 ㅜㅜ [2]파일 다운로드1
1141나그네5/10/20136038안녕하세요..전자세금 관련하여.. 보다보다 여기까지 왔습니다. [4]
1140이성환5/8/20136324제네릭과 배열 관련 기초 질문입니다. [11]
1139양영석5/3/20135078페이스북 로그인 관련하여.... [1]
1137김민현4/30/20135306Binary 구조체에 대한 Linq query 관련 질문입니다. [3]파일 다운로드1
1136박상영4/25/20136040ksc5601에서 지원하지않는 확장문자 찾는방법 [3]
1135sequel24/16/201320115c# dll c++ 에서 사용하기 (event 사용) [17]
1134이성환4/12/20134714volatile 키워드 관련 질문입니다. [3]파일 다운로드1
1133서광석4/8/20138072vb.net 2010에서 배포시 문제점 확인 방법에 대한 질문드립니다 [1]
1131헤딩3/28/201310692IIS 처리 성능을 높이고 싶으면 풀의 proecss 수를 계속 늘리면 될까요? [1]
1130박재율3/27/20135771WCF 서비스가 종종 죽는데 도움좀 부탁드립니다 ㅠ [1]
1129이상민3/22/201310343안녕하세요...IE Embedded Smart Client가 Windows8에서 정상동작을 하지 않습니다. [8]
1125박홍균3/19/20134783안녕하십니까. visual studio 에서 TFS 접속 관련으로 궁금한 점 여쭤봅니다.
1124궁금이3/18/20137441IIS 서버 이중화 구성시 데이터 베이스 트랜잭션 관리에 관해서... [1]
1127궁금이3/19/20135573    답변글 [답변]: IIS 서버 이중화 구성시 데이터 베이스 트랜잭션 관리에 관해서...(정성태님 봐주세요)
1122김시현3/11/20137188 Xamarin 에 관련하여 질문드립니다. [4]
1121롬메달2/14/20136276Assembly.Load로 얻은 객체안에서 제3의 어셈블리를 참조할때의 문제입니다. [8]
... 31  32  33  34  35  36  37  38  39  40  41  42  [43]  44  45  ...