C#

using c# dll in c++

7월 15, 2016 C# No comments

1.기존 C# DLL을 COM으로 노출 시켜야 함

  • (1)프로젝트->속성->Assembly 정보 -> 어셈블리를 COM에 노출 check

  • (2)프로젝트->속성->Build->COM Interop 등록 check

2.노출하고자 하는 Function을 포함하는 Interface를 구현

[ Guid ("8F42A31B-7C69-4E14-BF7A-56F243134852" )]
public interface INDTComponent
{
    int ShowTGCControl(byte[] tgcValue);
}

3.해당 Interface를 상속하영 Class구현

[ Guid ("379AA4DF-991F-4A72-B091-9DCEE25D8DD5" )]
public class NDTComponent : INDTComponent
{
    public int ShowTGCControl( byte [] tgcValue)
    {
        frmImageAdjustmentCurve form = new frmImageAdjustmentCurve ();           
        form . SetTGCValue(tgcValue);
        form . ShowDialog();           
        return 1 ;
    }
}

4.C# DLL을 Build시 *.tlb 생성됨

5.C++ 프로젝트에서 연동하고자 하는 C# DLL의 tlb파일을 Import

#import "..\Bin\Release\NDTComponent.tlb" no_namespace named_guids

(참고)C++ 프로젝트 Compile후 tlh가 자동 생성됨

6. C# DLL을 호출

NDTComponentPtr ptr ( __uuidof( NDTComponent ));
ptr->ShowTGCControl();
ptr->Release();

– Array 전달하기 : SAFEARRAY를 이용하여 전달

NDTComponentPtr ptr ( __uuidof( NDTComponent ));

uchar tgcValue [ 256];
memset ( tgcValue, 0, sizeof( tgcValue ));

SAFEARRAYBOUND sab ;
sab . lLbound = 0;
sab . cElements = 256;
SAFEARRAY * pSA = SafeArrayCreate (VT_UI1 , 1 , &sab );

LONG index ;
for (int i =0 ; i<256 ; i ++)
{
     tgcValue [ i] = NDTData :: getInstance(). m_TGCValue [i ];          
     index = i;
     SafeArrayPutElement ( pSA, &index , &tgcValue [i ]);
}      

ptr->ShowTGCControl(pSA);
ptr->Release();

callback 함수 전달하기

1.C# DLL에 callback용 Delegate 선언

[UnmanagedFunctionPointer( CallingConvention.StdCall)]
public delegate void CurveChangedCallback(byte [] curveValue);

2.C# DLL에 callback Parameter 추가

int ShowTGCControl([MarshalAs(UnmanagedType .FunctionPtr)]CurveChangedCallback callback);

3.C++에 callback용 함수 선언 및 구현

static NDTComponentCOM::CurveChanged(uchar* tgcValue);

4.C++에서 Parameter로 전달 (long)형으로 casting

void * curveCallback = NDTComponentCOM:: CurveChanged ;
ptr->ShowTGCControl( nX , nY , pSA, (long ) curveCallback);
ptr->Release();

struct parameter in c++ dll function

7월 15, 2016 C# No comments

struct parameter를 C++ dll과 C# 간의 parameter 전달 방식을 정리 했습니다.

1.C++에서 Struct 선언

typedef struct tag ULTRASOUNDREGION {
             char RegionDataType[LEN_US + 1];
             float PhysicalDeltaX;
} ULTRASOUNDREGION

typedef struct tag DICOMINFO{
             char SOPClassUID[LEN_UI + 1];
             ULTRASOUNDREGION UltrasoundRegion[ 5 ];
} DICOMINFO

2.C#에서 Struct 선언

[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct ULTRASOUNDREGION
{  
        //배열을 전달시 UnmanagedType을 ByValArray로 설정 후 SizeConst에 배열의 크기(Index Count) 를 전달
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = VR.LEN_US + 1)]
        public char[] RegionDataType;

        [MarshalAs(UnmanagedType.R4)]
        public float PhysicalDeltaX;

        //선언 배열은 Struct 생성자(c# sturct는 최소1개의 Parameter 필요)에서 할당
        public ULTRASOUNDREGION(bool bInit) : this()
        {
            RegionSpatialFormat = new char[VR.LEN_US + 1];
        }
}

[Serializable]
[StructLayout( LayoutKind .Sequential, CharSet = CharSet.Ansi)]
public struct DICOMINFO
{  
        //구조체 배열도 동일하게 ByValArray로 설정, SizeConst는 구조체 배열의 Index Count로 설정
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5 ]
        public ULTRASOUNDREGION[] USRegion;

        //배열이 아닌 그냥 구조체로 전달시에는 UnmanagedType.Struct로 설정하여 전달, SizeConst는 Struct 실제 Size
        Ex)
        [MarshalAs(UnmanagedType.Struct, SizeConst = 40]
}

3.C++ DLL Function의 Parameter에 구조체 Pointer전달

Ex)
extern "C" DLLDECL int SaveDICOMFile( DICOMINFO *pDicomInfo , char *pBMPFilePath, char *pDICOMFilePath);

4.C# Module 선언부분에 구조체에 ref 추가

Ex)
[ DllImport("DCMTKInterface.dll", CallingConvention = CallingConvention .Cdecl)]
public static extern int SaveDICOMFile( ref DICOMINFO dicomInfo, char [] bmpFilePath, char [] dicomFilePath);

5.C#에서 C++로 return 받고자 하는 Struct Array를 전달시

C#)
void LoadNDTRawFile(NDTParam10[] pNDTParam);

c++)
void LoadNDTRawFile(NDTParam10* pNDTParam);

C++에서 data관련 처리후 pNDTParam[0] or pNDTParam[index]를 통해 값을 설정하면 C#측은 설정된 NDTParam10 array를 받을 수 있다.

c++ dll function call

7월 15, 2016 C# No comments

C++ DLL에서 생성한 Instance를 Parameter로 Return 받고자 할때

1.C++ DLL에서 void** pInstance형으로 전달 받기

Ex) int LoadDICOMImage( void ** hImage)
{
    *hImage = (void*)new DicomImage();
}

2.C#에서 해당 DLL Function 선언시 ref IntPtr로 설정

Ex) [DllImport(“DCMTKInterface.dll”)]
    public static extern int LoadDICOMImage( ref IntPtr hDicomImage)

3.C# Main에서 해당 함수 호출시 IntPtr 전달 및 ref 설정

Ex) IntPtr hDicomImage = new IntPtr();
    DCMTKInterface.LoadDICOMImage( ref hDicomImage)

C++ DLL에 long과 같이 일반자료형 call by reference로 return 받기

1.C++ DLL에서 long*형으로 전달 받기

Ex) int GetImageBufferLength(void* hDicomImage, long* nBufferSize)

2.C#에서 해당 DLL Function 선언시 ref long로 설정

Ex) [DllImport(“DCMTKInterface.dll”)]
public static extern int GetImageBufferLength( ref long nBufferSize)

3.C# Main에서 해당 함수 호출시 long 전달 및 ref 설정

Ex) long nBufferSize = 0;
   DCMTKInterface.GetImageBufferLength(hDicomImage, ref nBufferSize);

C++ DLL에 byte[]같이 배열을 전달하여 값 삽입후 return 받기

1.C++ DLL에서 byte*형으로 전달 받기, Array size도 같이 전달

Ex) int GetImageBuffer(void* hDicomImage, byte* pBufferData, long nBufferSize)

2.C#에서 해당 DLL Function 선언시 byte[]로 설정

Ex) [DllImport(“DCMTKInterface.dll”)]
public static extern int GetImageBuffer (IntPtr hDicomImage, byte[] pBufferData, long nBufferSize);

3.C# Main에서 해당 함수 호출시 Memory 할당 후 전달

Ex) byte[] pBuffer = new byte[nBufferSize];
DCMTKInterface. GetImageBuffer (hDicomImage, pBuffer, nBufferSize );

byte[] to Struct

7월 15, 2016 C# No comments

Marsharing을 이용하여 memory copy형식으로 변환하는 예제입니다.

대신 struct에 [Serializable] 추가 해주셔야 합니다.

byte[] to Struct

IMAGEHEADER header = new IMAGEHEADER ()
int size = Marshal.SizeOf(header);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(arr, 0, ptr, size);
header = ( IMAGEHEADER) Marshal .PtrToStructure(ptr, header.GetType());
Marshal.FreeHGlobal(ptr);

struct to byte[]

int size = Marshal .SizeOf(header);
byte [] arr = new byte[size];
IntPtr ptr = Marshal .AllocHGlobal(size);

Marshal.StructureToPtr(header, ptr, true);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);

Reactive Extensions(Rx)

7월 15, 2016 C# No comments

Reactive Extensions(Rx : https://msdn.microsoft.com/en-us/data/gg577609.aspx)

1.간단한 background 작업 및 작업 완료 기다리기(Async)

Debug. WriteLine( "Shows use of Start to start on a background thread:" );
_Observer = Observable . Start(() =&gt;
{
    //This starts on a background thread.
    Debug .WriteLine( "From background thread. Does not block main thread." );
    Debug .WriteLine( "Calculating..." );
    Thread .Sleep( 5000 );
    Debug .WriteLine( "Background work completed." );
});
_Observer . Wait();
Debug .WriteLine( "Form1() End." );

Reactive Framework인데 생각보다 사용법도 복잡하고, 최근에는 update도 잘 이루어 지지 않아 비추 합니다.

하지만 이런게 있다는 것만 알면 될듯 하네요.

convert char[] to string problem

7월 15, 2016 C# No comments

Fixed된 char[]을 string으로 변환시 \0이 포함되는 문제가 발생됩니다.

char chBuffers[128];
Copy(Chbuffers, “test123”);
String strBuffers = new string(chBuffers);
//strBuffers 에는 “test123\0\0\0\0\0”이 들어가 있음
//strBuffers 에는 “test123\0\0\0\0\0.dcm”이 들어가 있음
strBuffers += “.dcm”;
  • 아래와 같이 Trim(‘\0’)을 수행하여 제거 해 주면 됩니다.

    Encoding .Unicode. GetString(patientName).Trim( ‘\0’);

Binary File Read faster

7월 15, 2016 C# No comments

기존 BinaryReader를 통한 read보다 .Net Framework 4.0에서 새로 지원하는 MemoryMappedFile로 Read시 CPU Performance와 속도가 빠릅니다.

ex) MemoryMappedFile을 이용하여 Read

Reference가 없는 Struct는 자체 Generic Function으로 지원

Reference가 있는 것은 하단과 같이 변환

using( var  mmf = MemoryMappedFile. CreateFromFile(fileName,  FileMode.Open, "MemoryFileID"   ))                                    
{
   using  ( var  accessor =  mmf.CreateViewAccessor())
   {
        long  pos  =  0 ;
        index = accessor.ReadInt32(pos);
        pos  +=  sizeof(Int32);
        dataCount = accessor.ReadInt32(pos);
        pos  +=  sizeof(Int32);

        BookParam = BookStruct.FromMemoryFile(accessor, pos);
        pos += Marshal.SizeOf(typeof   (  BookParam10 ));
        PageParams  =  new PageParam[channelGroupCount];
        int  pageRawSize  =  0;
        int  pageRawTotalSize  =  0 ;
        for  ( int  chn  =  0  ; chn  &lt;  channelGroupCount; chn ++  )
        {
            accessor.Read &lt;PageParam&gt; (pos, out PageParams[chn]);
            pos  +=  Marshal.SizeOf(typeof(PageParam));
            pageRawSize = PageParams[chn].PageCount *  BookParam  .   PageDataLength;
            pageRawTotalSize += pageRawSize;
        }  
        accessor.ReadArray&lt;short&gt;(pos, pPageRawBuffer, 0, pageRawTotalSize);
   }
}