Microsoft MVP성태의 닷넷 이야기
C#에서의 함수포인터 [링크 복사], [링크+제목 복사]
조회: 10008
글쓴 사람
왕초보
홈페이지
첨부 파일
 
안녕하세요? 성태님.
카메라 한대 사서 프로그래밍중인데요...ㅠㅠ~ 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#으로 돌리고 싶네요.
귀중한 시간 자꾸 앗아서 죄송합니다.






[최초 등록일: ]
[최종 수정일: 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를 동일하게 넘겨보고 문제 발생 지점을 축소해서 재현해 보세요.

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

... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...
NoWriterDateCnt.TitleFile(s)
1550김선아4/15/20154285    답변글 [답변]: .net framework 4.5와 c++ dll에 관한 질문입니다 [2]
1547문명식4/3/20155316IWebBrowser Key Event 관련 [2]
1545나종식3/25/20154996internet explorer dns cache flush [2]
1543이용원3/23/20155763hyper-v 질문드려요! [1]
1544이용원3/24/20154698    답변글 한가지만 더 여쭤볼게요! [1]
1541임기성3/20/20159643Microsoft.Office.Core dll관련 문제 [1]
1539펜플3/17/20154802asp.net 질문입니다. [1]
1540113/17/20154948    답변글 [답변]: asp.net 질문입니다. [1]
1538열공합시닷3/13/20156286중계서버 질문드립니다 [1]
1537김재영3/11/20155485asp.net의 빌드 유휴시간 이후의 리빌드 작업을 컨트롤 할 수 있습니까? [2]
1536솔솔3/9/20155329mac address얻으려고 하는데요.. [1]
1535StudyMan3/4/20155826C#엔 MFC의 theApp 과 같은 기능을 하는것이 없나요? [3]
1534김동진3/4/20155101C#에서 BHO로 Windows Explorer에서의 동작이 가능할지요 [2]
1533김동현3/3/20154768컴파일 된 프로그램에 코드 삽입(?) 관련입니다. [1]
1532반가워요3/3/20156852C# 소켓연결에 packet송수신... [8]
1531강현수3/2/20156273C# com 등록 시 GUID 변경 관련 문의 [1]
1529김민규2/15/20156536C++ 64bit/32bit [3]파일 다운로드1
1528김개똥2/14/20154586좀 관련없는 질문이지만, 정말 답답하고 찾는데 명확한 해답 찾지못해 질문 올립니다. <Windows 사용자 인증> [1]
1527방문자1/27/20154872System Timer의 Tick에서 Backgroundworker를 호출하면 스레드 문제가 있을까요? [1]
1526황상대1/26/20155931C# 마샬링 관련 질문이 있습니다. [1]
1525Ji-young Kim1/19/20154273시작하세요 C# 프로그래밍 도서에 관한 질문입니다. [1]
1523임종복12/31/20149715windows 2008 r2에서 ms-sql 2008 서버사용시 tls문제점 [3]
1524임종복1/4/20156507    답변글 [답변]: windows 2008 r2에서 ms-sql 2008 서버사용시 tls문제점 [2]
1521윤대욱12/19/20145267C++ 에서 서버와 클라이언트를 나누어서 구조체 전달에 대해서 궁금합니다. [1]파일 다운로드1
152012/10/20144897datetime관련해서요 [1]
1516aTo12/1/20145344wpf 배포관련. [3]
... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...