Microsoft MVP성태의 닷넷 이야기
C#에서의 함수포인터 [링크 복사], [링크+제목 복사]
조회: 11169
글쓴 사람
왕초보
홈페이지
첨부 파일
 
안녕하세요? 성태님.
카메라 한대 사서 프로그래밍중인데요...ㅠㅠ~ 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)
3645spowner11/13/20156130    답변글 [답변]: c# user.config파일 - Json.NET을 이용한 설정파일 처리 [1]파일 다운로드1
3643힘찬도약11/11/20157631C# 함수의 processing time과 재호출 [14]
3642.net11/10/20156283c# 으로 작성된 com+ 에 대한 문제입니다. [2]
3641힘참도약11/9/20156886c# log file 관련해서 질문드립니다. [5]
3638윤창선11/4/20156726사설IP가 부여된 무선라우터간 영상전송 관련 문의 [8]
3634Hyun Su An11/2/20155396c# 에서 webkit browser에서 webgl을 이용하는 사이트에 접속이 안됩니다. [1]
3633힘찬도약10/31/20155764mysql insert where not exists [6]
3632힘찬도약10/27/20156261C# Lock 관련해서 질문드립니다. [6]
3655iwc11/30/20154836    답변글 [답변]: C# Lock 관련해서 질문드립니다.
3631강준10/26/20156720iis 8.5 preload 기능에 대해 질문이 있습니다. [11]
3630김정훈10/25/20155278몬티홀 게임 관련 질문 [1]
3629pooq10/23/20155767리플렉션 관련해서 질문 입니다. [3]
3628최영민10/22/20155160스마트 클라이언트 로딩속도 문의입니다. [3]
3627양주호10/22/20155267C#으로 컨버팅 하려고 하는데요... [1]
3626조성진10/21/20156223책보고 첫번째 예제부터 문제가 생기네요 ^^; [4]파일 다운로드1
3623Beren Ko10/19/20155514질문이라기 보단... [2]
3625Beren Ko10/20/20155585    답변글 [답변]: 질문이라기 보단... [2]파일 다운로드1
3621힘찬도약10/18/20155708[C# 6.0]multi threading과 ui control [9]
3624힘찬도약10/19/20156022    답변글 [답변]: [C# 6.0]multi threading과 ui control [6]파일 다운로드1
3620popo10/13/20154982WPF의 datagrid, listview 컨트롤 관련 질문 입니다. [1]
3619링크의 전설10/12/20158597OCX 로드 관련 질문입니다. [5]파일 다운로드1
3616수요일밥시간10/7/20158546몇 가지 오류 (2) [6]
3615김응규10/7/20155156다시한번 질문 드립니다. (이번엔 자세하게 기술했습니다.) [1]
3614김응규10/6/20154327안녕하세요. wcf net.tcp 관련 질문 하나만 올려요~~ [4]
3613강준10/5/20157294IIS Application Pool 시작/중단 에 대한 이벤트 로그는 어디에 남나요??? [2]
3612심심한일요일10/4/20158484몇 가지 오류 [4]
... 31  [32]  33  34  35  36  37  38  39  40  41  42  43  44  45  ...