DLL을 C#에서 사용하기

C# 2018. 10. 25. 11:19

__declspec( dllimport ) void SetTimeOut(int IN nTimeOut);
이 형태는 int 값을 DLL내부 함수에 넘겨주는 것이다.

C#에서 호출 할때는 다음과 같다.
[DllImport("DLL명")]
private static extern void SetTimeOut(int nTimeOut);
C++이나 C#이나 Type이 동일하므로 크게 볼 내용은 없다. 똑같은 변수 타입으로 해주면 된다.


다음은 조금(?) 어렵다.
__declspec( dllimport ) int Connect(
         TCHAR IN *pszIp,
         int IN nPort,
         void OUT **ppContext
);
결과값으로 int형을 리턴하고 문자열과 숫자를 입력 받고 void **를 받는 형태이다.

여기서 주의 할것은 eVC는 VS6와는 다르게 유니코드가 기본값이라는 것이다.
Connect(이 함수는 서버에 접속한다고 가정한 함수)일경우 아이피와 포트 그리고 그 핸들의 포인터를 받아 처리를 많이 하는 경우를 가정한것인데..(아닌가?^^;)

C나 C++은 C#에 비하면 변수 타입에 대단히 관대하기 때문에 void *형을 개인적으로 많이 사용을 햇는데 C#에서는 그것이 발목을 많이 잡았다.
특히 **를 받아서 DLL내부에서 new나 malloc을 하는 경우와 문자열값을 UniCode에서 AnsiCode로 변경하는 경우는 eVC에서 부터 많이 접해봤을것이다.

[DllImport("DLL명", CharSet = CharSet.Unicode )]
private static extern int Connect(
            [MarshalAs(UnmanagedType.LPWStr )] System.Text.StringBuilder  szip
            , int nport
            , ref IntPtr pContext
);

TimeOut함수에 비하면 조금 복잡하다.

서버 아이피 값을 넘겨주는 경우 eVC에서 _T , L , _TEXT Macro를 사용했을 것이다.
정 확한 이유는 모르겠지만, C#으로 Win32가 아닌 Mobile프로젝트를 구성하는 경우 당연히 Unicode가 기본이 될것이므로 string Type을 쓰더라도 상관 없을것이라 생각했는데 ... TCHAR *로 되어 있는 경우 char *와는 다른게 UnmanagedType을 정확히 명시 해줘야 한다는 것이다.
StringBuilder의 경우 string으로 해도 무관하지만 string보다 장점이 많다고 하여 써본것이다.
StringBuilder는 string으로 대체해도 된다.

UnmanagedType은 다음과 같다.
Bool : 4바이트 불리언값
ByValArray : 고정길이 배열
FunctionPtr : 함수 포인터
I1 : 1바이트 부호화 정수
I2 : 2바이트 부호화 정수
I4 : 4바이트 부호화 정수
I8 : 8바이트 부호화 정수
LPStr : Ansi문자열
LPStruct : C언어 구조체 포인터
LPTStr : 플랫폼 독립적인 문자열. Windows98계열은 Ansi문자열 Windows2000계열은 Unicode 문자열
LPVoid : 타입이 없는 4바이트 포인터
LPWStr : 유니코드 문자열
R4 : 4바이트 부동 소숫점
R8 : 8바이트 부동 소숫점
Struct : C언어 구조체
SysInt : 플랫폼 독립 부동화 정수. 32비트 OS의 경우 4바이트 64비트 OS일 경우 8바이트
U1 : 1바이트 비부호화 정수
U2 : 2바이트 비부호화 정수
U4 : 4바이트 비부호화 정수
U8 : 8바이트 비부호화 정수

첫번째 인자가 TCHAR * 의 문자열을 받으므로 UnmanagedType을 LPWStr로 선언을 해주었다.
두번째 인자 숫자는 TimeOut과 마찬가지로 int로 쉽게 해결하면 된다.
세번째 인자는 void **를 받아서 DLL내부에서 malloc하는 경우 void **로 동일 하게 명시해줘도 상관 없지만, C#에서 void *의 &를 넘겨주기가 좀 애매하다.
C#에서 void *를 대신하는(맞는지 정확히는 모르겠다...이놈의 모래성 ㅠㅠ) IntPtr과 Call Reference의 기호 ref로 해결을 보았다.


문자열이 포함된 구조체는 어찌 보내나???

char szMsg[128];
int nCnt;

이런 식으로 선언된 구조체 말이다...!!! 


못보낸다... ㅜㅡ

보내고 싶으면 정말 이렇게 해야 한다.
쌩 노가다!!! 


.NET Framework 개발자 가이드
플랫폼 호출 래퍼 예제

구조체에 단순 형식이 포함된 경우에는 개체를 네이티브 함수에 전달할 수 있으며, 이 경우 네이티브 루틴은 .NET Compact Framework에서 구조체를 압축하는 방식을 따라야 합니다.

매개 변수, 구조체에 대한 포인터를 하나씩 사용하는 DoRequest라는 네이티브 함수가 있다고 가정합니다. 구조체는 다음과 같은 필드 두 개를 정의합니다.

  • NULL로 끝나는 ANSI 문자열이 포함된 문자 배열

  • 정수

다음은 C++의 구조체입니다.

typedef struct 
{
    char *RequestName,
    int   Count,
} ALERT_REQUEST;

int DoAlertRequest(ALERT_REQUEST *pRequest);

C#에서 이 구조체의 관리되는 버전은 다음과 같습니다.

struct AlertRequest
{
    String RequestName;
    int    Count;
}

class AlertClass
{
    [DLLImport("ALERT.DLL", EntryPoint="DoAlertRequest", 
        CharSet=CharacterSet.Ansi)]
    public static extern int DoRequest(ref AlertRequest Req);
}

DllImportAttribute 특성은 네이티브 메서드 호출의 메타데이터를 나타내며, DoRequest 메서드의 위치(ALERT.DLL), 메서드의 이름(DoAllertRequest) 및 문자열을 ANSI로 마샬링(관리 코드의 모든 문자열은 유니코드임)해야 함을 공용 언어 런타임에 알립니다.

.NET Framework에서 C++를 사용하는 경우 이 메서드에 대한 호출은 다음과 같습니다.

AlertRequest Request = new AlertRequest()
Request.RequestName = "beep";
Request.Count = 10;
AlertClass.DoRequest(ref Request);

.NET Framework의 공용 언어 런타임에서는 DoRequest 호출을 발견하면 ALERT.DLL을 동적으로 로드하고DoAlertRequest의 주소를 가져온 다음 필요에 따라 문자열을 변환하여 구조체의 관리되지 않는 버전을 빌드합니다. 그런 후에 이 구조체에 포인터를 푸시하고 DoAlertRequest를 호출합니다. 이 구조체에는 포함 String 개체가 들어 있기 때문에 구조체에 대한 포인터를 전달할 수 없습니다.

이러한 제약 조건을 고려할 때 네이티브 DoRequest 메서드를 직접 호출할 수 없으며 썽킹 호출을 대신 사용하여 DoRequest를 래핑해야 합니다. 다음은 C# 코드 예제입니다.

class AlertClass
{
    [DllImport("ALERT.DLL", EntryPoint="DoAlertRequestThunk")]
    private static extern int DoRequestThunk(String RequestName, int Count);

    public static int DoRequest(ref AlertRequst Req)
        {
            return DoRequestThunk(Req.RequestName, Req.Count);
        }
}

AlertClass에는 .NET Framework에서 상응하는 클래스와 시그니처가 같은 메서드가 포함됩니다. .NET Compact Framework의DoRequest는 전용 네이티브 루틴을 호출하는 관리되는 루틴입니다. 위의 코드 예제에서 구조체 내의 String 개체는 마샬링될 수 없으므로 네이티브 루틴에 대한 호출에 대해 구조체의 각 필드가 별도의 인수로 분리됩니다. 썽크는 네이티브 DoRequest를 래핑하며 구조체의 관리되지 않는 버전을 빌드하고 문자열 변환을 수행합니다(아래의 C++ 코드 참조).

int DoRequestThunk(wchar_t *RequestNameW, int Count)
{
    ALERT_REQUEST Req;
    int ReturnCode;
       
    // CreateAnsiFromUnicodeString allocates and builds an ANSI
    // version of the Unicode string.
    Req.RequestName = CreateAnsiFromUnicodeString(RequestNameW);
    if (Req.RequestName == NULL) 
        Return 0;

    Req.Count = Count;

    // This is the native DoRequest, not to be confused
    // with the managed method of the same name.
    ReturnCode = DoAlertRequest(&Req);

    free(Req.RequestName)
    return ReturnCode;
}

참조 : http://www.heart4u.co.kr/tblog/194


체크 할 것

 .Net 2.0 서비스 팩 버전 확인


http://bestofsky.com/48



0. (함수 인자에 참조형을 사용 할 경우?,) 

   해당 소스 코드 프로젝트 속성에서 '빌드 > 안전하지 않은 코드 허용'에 체크한 후,

   unsafe { } 구문으로 감싸준다.

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNo=8&no=125908&ref=125908



1. 선언한 배열의 크기보다 더 큰 데이터가 전달되지는 않았는지 확인한다.

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNo=8&no=61219&ref=61195



2. C#에서 생성하지 않았거나 크기가 명확하지 않은 포인터를 직접 참조하지 않았는지 확인한다.

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNo=8&no=150988&ref=150961







'C#' 카테고리의 다른 글

Label 투명하게!  (0) 2018.12.10
해쉬테이블 (Hashtable)  (0) 2018.11.08
c#에서 비관리코드를 호출하는 방법  (0) 2018.10.24
[MFC] LPSTR,LPCSTR,LPCTSTR .. 과연 무엇인가?  (0) 2018.10.23
opos visual studio 적용....  (0) 2018.10.15
블로그 이미지

벵거빠돌이

,

Win32 API 사용하기



C#에서 Win32 API 사용하기


개요

Win32 API를 불러올 때, 함수의 명칭, 인자, 리턴 값을 가지고 불러오게 되어 있다. 하지만, C#에서 타입들이 모두 객체(Object)의 형식이며, 일반적인 C 의 데이터 형과 상이한 모양을 가진다. 이러한 문제들을 해결할 수 있는 것이 PInvoke 기능이다.


PInvoke( Platform Invocation Service)는 관리화 코드에서 비관리화 코드를 호출할 방법을 제공한다. 일반적인 용도는 Win32 API의 호출을 위해 사용한다.


namespace PinvokeExample

{

using System;

        using System.Runtime.InteropServices; // 반드시 입력해야 한다.

        public class Win32

        {

                [DllImport("user32.dll")]

                public static extern int FindWindow(string a, string b);

                

        }

}



위 예제는 FindWindow라는 user32.dll의 C함수를 사용하는 모습을 보여주고 있다. 실제 FindWindow의 선언은 다음과 같다.


        HWND FindWindow(LPCSTR swClassName, LPCSTR swTitle);

HWND는 윈도우 핸들을 표현하는 32비트 정수 이므로, int형으로 치환되고 LPCSTR 형은 NULL로 끝나는 문자열을 표현한다. 이때 PInvoke는 string을 자동으로 LPCSTR로 치환해 주는 역할을 하게 된다.

이 문서에서는 이처럼 Win32 API 함수의 여러 유형들을 어떻게 C#에서 사용 할 것인지에 대하여 알아보자.


WIN32 데이터형의 치환

 

Win32 API에서 일반적으로 사용하고 있는 데이터형은 모두 C#의 데이터 형으로 치환될 수 있다.


WIN32

비관리C데이타타입

C#

HANDLE

int

int

BYTE

unsigned char

byte

SHORT

short

short

WORD

unsigned short

ushort

INT

int

int

UINT

unsigned int

uint 또는 int

LONG

long

int

BOOL

long

int

DWORD

unsigned long

uint

ULONG

unsigned long

uint

CHAR

char

char

LPSTR

char*

string 또는 StringBuilder

LPCSTR

const char*

string

LPWSTR

wchar_t*

string 또는 StringBuilder

LPCWSTR

const wchar_t*

string

FLOAT

float

float

DOUBLE

double

double

 

char * 

char[] 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 




Structure 의 전달

예를 들어 POINT 형의 경우,

typedef struct t_Point {

        int x;

        int y;

} POINT;



이것은 기본적으로 다음과 같이 선언될 수 있다.

[순차적]

[StructLayout(LayoutKind.Sequential)]

public struct Point {

      public int x


 

      public int y;

}


[명시적]

[StructLayout(LayoutKind.Explicit)]

public struct Point {

      [FieldOffset(0)] public int x;

      [FieldOffset(4)] public int y;

}

일차적으로 할당되는 메모리 레이아웃이 동일하다면, C#에서 바로 받아 들이 수 있다.


// BOOL SetWindowPos(POINT pos); 이런 함수가 있다고 가정하면 ^^


[DllImport ("user32.dll")]

public static extern bool SetWindowPos(Point pos);



사용할 함수 이름 바꾸기

여기서 함수의 이름을 바꿔서 사용하고 싶다면 다음과 같이 변경하면 된다.


// BOOL SetWindowPos(POINT pos);


[DllImport ("user32.dll", EntryPoint = "SetWindowPos")]

public static extern bool ShowAt(Point pos);레퍼런스형 전달하기


LPPOINT형은 POINT의 포인터 형이므로 ref Point와 같이 사용 할 수 있다. 실제 사용하는 형식은 다음과 같다.

C 언어의 포인터의 경우 레퍼런스로 사용하려고 하면, ref 키워드를 사용하는 방법이 있다.

// BOOL SetWindowPos(HWND hWnd, LPRECT lpRect);

[DllImport("user32.dll")]

public static extern bool SetWindowPos(int hWnd, ref Rect lpRect);



Out형 함수 인자 사용하기

MSDN 같은곳에서 함수의 선언을 살펴보면 다음과 같은 형식의 함수를 볼 수 있을 것이다. 이러한 형식은 레퍼런스 형으로 결과를 함수의 인자에 보내겠다는 말이다. 이러한 형식은 Win32 API에서 많이 쓰이고 있고, 포인터를 사용하므로, 많은 주의를 기울여야 한다.


BOOL GetWindowRect(

  HWND hWnd,      // handle to window

  LPRECT lpRect   // window coordinates


 

);



Parameters

hWnd

[in] Handle to the window.

lpRect

[out] Pointer to a RECT structure that receives the screen coordinates of the upper-left and lower-right corners of the window.


여기서 LPRECT는 앞 절에서 설명한 Structure의 전달을 참고하여 치환 될 수 있다.

여기서 lpRect는 RECT의 포인터이며, GetWindowRect 함수 내에서 이 포인터에 직접 값을 쓰게 되어 있다. 즉 이 포인터는 값을 기록하기 위한 인자이지, 값을 전달하기 위한 인자는 아닌 것이다. 이것은 또 다른 C# 레퍼런스 연산자인 out 키워드를 사용하여 쉽게 해결 할 수 있다.

public static extern bool GetwindowRect(int hWnd, out Rect lpRect);

실제 사용하는 모습은 다음과 같다.

public static extern bool GetWindowRect(int hWnd, out Rect lpRect);


public static void UseFunction() {

        Rect _rect; // 값을 대입하지 않아도 된다.

        Win32.GetWindowRect(hwnd, out _rect);


}


참고로 ref 키워드는 입력과 출력 둘 다 사용 할 수 있다. 그러나 ref를 사용하는 변수가 값이 설정되어 있다는 가정을 하고 있으므로, 이전에 반드시 어떠한 값을 입력해야 한다.

실제 사용 예는 다음과 같다.

public static extern bool GetWindowRect(int hWnd, ref Rect lpRect);


public static void UseFunction() {

        Rect _rect = new Rect(); // 꼭 값을 대입해야 한다.

       

        _rect.top = 20; _rect.left = 30;

        _rect.bottom = 50; _rect.right = 60;


        Win32.GetWindowRect(hwnd, ref _rect);


}




 




여기서 잠깐

대중없이 Rect라는 구조체가 나오는데 이는 API에서 RECT형을 C#으로 바꾸어 사용하는 structure이다. 앞의 예제들은 다음과 같은 선언을 하였다고 가정한다.

[StructLayout(LayoutKind.Explicit)]

public struct Point {

[FieldOffset(0)] public int top

[FieldOffset(4)] public int left

[FieldOffset(8)] public int bottom;

[FieldOffset(12)] public int right;

}




CALLBACK 함수의 선언

C 언어에서 콜백 함수는 함수 포인터로 존재하게 된다. 이것은 함수 인스턴스의 포인터로, 함수 자체를 전달하게 되는 방식이다. 대표적으로 사용되는 부분은 EnumWindows 함수이다.

// BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam)


이 함수는 현재 열려 있는 모든 윈도우 핸들을 열거하기 위한 함수로 실제 처리하는 부분은 함수 포인터, 즉 콜백함수인 lpEnumFunc에서 처리하게 되어 있다. WNDENUMPROC 타입의 선언은 다음과 같다.

// typedef BOOL (CALLBACK* WNDENUMPROC)(HWND, LPARAM);

public delegate bool Callback(int hWnd, long lParam);이러한 콜백 함수 역할을 하는 C#의 프리미티브는 delegate이다. CALLBACK은 delegate로 지환된다.


결과적으로 다음과 같이 사용하게 된다.

namespace ada.appshare

{


        public delegate bool Callback(int hwnd, int lParam);

        

        internal class Win32

        {

               

                internal static extern int EnumWindows(CallBack x, int y);

                [DllImport("user32.dll")]



 

   public static bool EnumWindowsCallback

          (int hWnd, int lParam)                         

  {

               System.Console.WriteLine(""+ hWnd);

               return true;

   }

                

}


        public static void Main(String []args)

        { 

             Win32.Callback call=

                 new Win32.Callback(Win32.EnumWindowsCallback);

             Win32.EnumWindows(call, 0);

        }

}





Win32추가설명


DllImport 애트리뷰트는 DLL익스포트 함수를 호출하는데 필요한 정보를 지정하는

몇가지 중 요한 필드 제공


[DllImport("DLL명", CharSet=CharSet열거형값)]

ChartSet

이필드에 CharSet.UniCode값이 지정되면 모든 문자열 인수는 유니코드문자로 변

환됩니다. 윈도우 98에서는 기본적으로 ANSI 문자를 사용합니다. 윈도우 2000에

서는 CharSet.Unicode 기본입니다.  지정하지 않으면 기본값으로 사용합니다.




MarshalAs 애트리뷰트


관리코드와 비관리코드사이에 메서드 호출이 이루어 질 때 , 이들 사이의

데이터에 대해서 마샬링이 발생합니다.  여기에는 인수로 넘겨주는 값 뿐만이

 아니라 반환되는 값도 포함합 니다.  마샬링 방법을 지정하기 위해서

사용합니다.  MashalAs 애트리뷰트는 다른 타입의 데이터를 마샬링 하는

방법을 결정합니다. 





 




[MarshalAs(UnmanagedType열거형값)]

멤버 이름

설명

AnsiBStr

싱글 바이트 길이 접두사가 있는 ANSI 문자열

AsAny

런타임에서 개체의 Type을 결정하고 해당 Type으로 개체를 마샬링하는 동적 형식

Bool

4바이트 부울 값(true != 0, false = 0)

BStr

더블바이트 길이 접두사가 있는 유니코드 문자열

ByValArray

MarshalAsAttribute.ValueByValArray로 설정된 경우, SizeConst는 배열에 있는 요소의 숫자를 나타내도록 설정되어야 합니다. ArraySubType 필드는 문자열 형식 사이에서 구별을 지을 필요가 있는 경우 배열 요소의 UnmanagedType을 포함할 수도 있습니다. 또한 이 UnmanagedType은 구조의 필드로 나타나는 배열에서만 사용될 수 있습니다.

ByValTStr

구조 내에 나타나는 인라인 고정 길이 문자 배열에 사용됩니다. 포함하는 구조체에 적용된 StructLayoutAttributeCharSet 인수에 의해 결정된 ByValTStr와 함께 사용된 문자 형식입니다.

Currency

Decimal 대신 COM 통화 형식으로 10진 값을 마샬링하기 위해 System.Decimal에서 사용됩니다.

CustomMarshaler

MarshalAsAttribute.MarshalType 또는 MarshalAsAttribute.MarshalTypeRef와 함께 사용될 때 사용자 지정 마샬러 클래스를 지정합니다. MarshalCookie 필드는 사용자 지정 마샬러에게 추가 정보를 전달하는 데 사용할 수 있습니다.

Error

I4 또는 U4와 관련된 기본 형식은 매개 변수가 내보낸 형식 라이브러리에 있는 HRESULT로 내보내지도록 합니다.

FunctionPtr

함수 포인터

I1

1바이트 부호 있는 정수

I2

2바이트 부호 있는 정수

I4

4바이트 부호 있는 정수

I8

8바이트 부호 있는 정수

IDispatch

COM IDispatch 포인터

Interface

COM 인터페이스 포인터. 인터페이스의 Guid는 클래스 메타데이터에서 얻을 수 있습니다.

IUnknown

COM IUnknown 포인터

LPArray

A C 스타일 배열입니다. 관리되는 배열에서 관리되지 않는 배열로 마샬링하면 배열의 길이는 관리되는 배열의 길이에 의해 결정됩니다. 관리되는 배열에서 관리되지 않는 배열로 마샬링하면 배열의 길이는 MarshalAsAttribute.SizeConstMarshalAsAttribute.SizeParamIndex 필드에 의해 결정됩니다. 문자열 형식 사이의 구별이 필요할 경우 배열에 있는 요소의 관리되지 않는 형식이 따라옵니다.

LPStr

싱글 바이트 ANSI 문자열

LPStruct

C 스타일 구조에 대한 포인터. 형식이 지정된 관리되는 클래스를 마샬링하는 데 사용됩니다.

LPTStr

플랫폼 종속 문자열, 즉 Windows 98에서는 ANSI, Windows NT에서는 유니코드입니다. LPTStr 형식의 문자열을 내보낼 수 없으므로 이 값은 플랫폼 호출에 대해서만 지원되고 COM interop에 대해서는 지원되지 않습니다.

LPWStr

더블바이트 유니코드 문자열

R4

4바이트 부동 소수점 수

R8

8바이트 부동 소수점 수

SafeArray

SafeArray는 형식, 차수, 관련된 배열 데이터의 범위를 전달하는 자동 기술 배열입니다.

Struct

형식이 지정된 관리되는 클래스와 값 형식을 마샬링하는 데 사용되는 C 스타일 구조

SysInt

플랫폼에 종속적인 부호 있는 정수. 32비트 Windows에서는 4바이트, 64비트 Windows에서는 8바이트입니다.

SysUInt

하드웨어에 종속적인 부호 없는 정수

TBStr

길이 접두사가 있는 플랫폼에 종속적인 Char 문자열. Windows 98에서는 ANSI, Windows NT에서는 유니코드입니다.

U1

1바이트 부호 없는 정수

U2

2바이트 부호 없는 정수

U4

4바이트 부호 없는 정수

U8

8바이트 부호 없는 정수

VariantBool

2바이트 OLE 정의 부울 값(true != -1, false = 0)

VBByRefStr

Visual Basic에서 관리되지 않는 코드의 문자열을 변경할 수 있으며 결과가 관리되는 코드에 반영되게 합니다.


 


예제


[C#]

using System;

using System.Runtime.InteropServices;


namespace MyModule

{

   // If you do not have a type library for an interface

   // you can redeclare it using ComImportAttribute.


   // This is how the interface would look in an idl file.


   //[

   //object,

   //uuid("73EB4AF8-BE9C-4b49-B3A4-24F4FF657B26"),

   //dual,   helpstring("IMyStorage Interface"),

   //pointer_default(unique)

   //]

   //interface IMyStorage : IDispatch

   //{

   //   [id(1)]

   //   HRESULT GetItem([in] BSTR bstrName, [out, retval] IDispatch ** ppItem);

   //   [id(2)]

   //   HRESULT GetItems([in] BSTR bstrLocation, [out] SAFEARRAY(VARIANT)* pItems);

 

   //   [id(3)]

   //   HRESULT GetItemDescriptions([in] BSTR bstrLocation, [out] SAFEARRAY(VARIANT) ** ppItems);

   //   [id(4), propget]

   //   HRESULT get_IsEmpty([out, retval] BOOL * pfEmpty);

   //};


   // This is the managed declaration.


   [ComImport]

   [Guid("73EB4AF8-BE9C-4b49-B3A4-24F4FF657B26")]

   public interface IMyStorage 

   {

      [DispId(1)]

      [return : MarshalAs( UnmanagedType.Interface )]

      Object GetItem( [In, MarshalAs( UnmanagedType.BStr )] String bstrName );


      [DispId(2)]

      void GetItems( [In, MarshalAs( UnmanagedType.BStr )] String bstrLocation,

         [Out, MarshalAs( UnmanagedType.SafeArray,

                 SafeArraySubType = VarEnum.VT_VARIANT )] out Object[] Items );

               

               

      [DispId(3)]

      void GetItemDescriptions( [In] String bstrLocation,

         [In, Out, MarshalAs( UnmanagedType.SafeArray )] ref Object[] varDescriptions );


      bool IsEmpty

      {

         [DispId(4)]

         [return : MarshalAs( UnmanagedType.VariantBool )]

         get;

      }

   }

}




고정길이 문자열 버퍼의 사용


 

경우에 딸사서는 고정길이 문자열 버퍼를 DLL 익스포트 함수의 인수에 넘겨주어야 하는 경

우가 있습니다. 예를 들어 윈도우 운영체제를 설치한 시스템 디렉토리의 경로명을 구하는

GetSystemDirectory API함수는 다음과 같은 원형을 갖습니다.



UNIT GetSystemDerictory (

   LPSTR lpBuffer,   // 경로병을 저장할 버퍼

   UINT uSize);      // 디렉토리 버퍼의 크기



이때 GetSystemDirecotry 함수의 첫 번째 인수에는 256 바이트 크리글 갖는 시스템 디렉토

리 경로명을 저장할 버퍼가 지정되어야 합니다. 이때 우리는 이 인수에 StringBuffer클래스

타입을 지정할 수 있습니다.


[ DllImport("kernel32.dll", CharSet=CharSet.Auto)]

public static extern int GetSystemDirectory(

   [MarshalAs(UnmanagedType.LPWstr, SizeConst=256)]

   StringBuilder buffer,

   int size);



우리는 이 함수를 다음과 같이 호출할 수 있습니다.


SrgingBuilder buffer = new StringBuilder(256);

Win32API.GEtSystemDirectory(buffer, buffer.Capacity);

Console.WriteLine(buffer);

'C#' 카테고리의 다른 글

해쉬테이블 (Hashtable)  (0) 2018.11.08
DLL을 C#에서 사용하기  (0) 2018.10.25
[MFC] LPSTR,LPCSTR,LPCTSTR .. 과연 무엇인가?  (0) 2018.10.23
opos visual studio 적용....  (0) 2018.10.15
C#으로 LS PLC와 RS232C 통신하기  (0) 2018.04.05
블로그 이미지

벵거빠돌이

,

LPSTR,LPCSTR,LPCTSTR,LPWSTR ... 비슷한게 많다.

이러한 것들은 string 처리를 위해 나온 표현이다.
단어는 다르지만 비슷한 알파벳들이 붙어서 하나의 표현이 되는데
각 단어마다 다른 의미를 가지고 있다.

LP
:long pointer의 약자로 컴파일러에 따라 다르지만
 .Net의 경우 64bit pointer, 다른 낮은 버전은 32bit pointer를 나타냅니다.

C
: constant의 약자로 변경을 하지 말라는 의미로 사용됩니다.

STR
: STR은 대충 짐작하겠지만
  string자료가 될것이고 내부적으로는 char형 배열에 마지막 종료값 null을 가지고 있습니다.

W
:wide char를 나타내고 unicode 입니다.
 

요약을 해보자면

LPSTR = long pointer string = char *
LPCTSTR = long pointer constant string = const char *

=>  const가 있냐 없냐 차이만 있을뿐 LPSTR , LPCTSTR은 같은 형태

LPWSTR = long pointer wide string = w_char *
LPCWSTR = long pointer constant wide string = const w_char *

=>  이것도 const만 빼면 같은 형태

T
: LPCTSTR => long pointer constant t_string = const tchar *
: tchar의 약자

tchar를 알기 위해서는 배경을 알고 있어야 합니다.
마이크로소프트가 제공하는 c 컴파일러 visual studio 기준으로
여러나라에 제품을 팔아야 하는데 나라의 언어에 맞게
개발하기에는 너무 효율성이 떨어진다고 판단하여
unicode 기반으로 개발을 시작했습니다.

그런데 char형식은 1Byte를 사용하고 wide char형식은 2Byte를 사용하기 
때문에 호환성의 문제가 많다고 판단하여
컴파일러가 precompile option을 보고 환경에 맞게 
컴파일 할수 있도록 변수를 만들게 됩니다.
그것이 바로 t_char(TCHAR) 변수 입니다.

visual studio 속성에서 환경을 멀티바이트로 할것인지 유니코드 기반으로 할것인지
설정할수 있는데 멀티바이트의 경우 char형으로 , 유니코드 기반일 경우 w_char형으로 
자동 type casting(형변환)이 됩니다.
요즘은 프로젝트를 만들게 되면 유니코드 기반으로 작업을 하는데
적어도 필자의 경우 증권시스템을 만드는데 회사의 솔루션이 멀티바이트 버전이여서 
얼마전에 솔루션을 유니코드 버전으로 바꾸느라 알게된 지식들입니다.

프로젝트 만들때 꼭 유니코드로 만드세요...나중에 유지보수가 너무 힘들어집니다.
멀티바이트에서 유니코드로 바꿀때...엄청난 노가다....ㅠ

MFC는 c,c++ 문법에 기반하다보니
char *을 안쓸수가 없는데 CString을 쓰다보면 아무래도 char *으로 형변환 해야 할때가 있다.
그럴때 (LPSTR)(LPCTSTR)CString 형식으로 사용해야 합니다.
=> 결국 (char *)(const char *)CString 입니다.
이렇게 사용하지 않으면 에러가 납니다.

그런데 사실 const라는것이 괜히 존재하는 것이 아니고 또한 const char *형으로 
CString을 받는것도 안정성을 위해 const를 붙여서 받는것인데
const를 제거하기 위해 char *로 다시 변환하게 되면 
나중에 코딩을 하다 큰 오류를 일으킬수 있습니다.

그렇기 때문에 보통
CString test = "abc";
char * ch = (LPSTR)(LPCTSTR)test;
AfxMessageBox(ch); 의 형식으로 데이터를 뽑아오지만

아래와 같이 사용하는게 더 안전하다고 합니다.
CString test = "abc";
char * ch = test.GetBuffer(0); //GetBuffer(0) 의미 : test가 가지고 있는 문자열 만큼 가지고 온다  
AfxMessageBox(ch);  


블로그 이미지

벵거빠돌이

,



공식 트위터에 트윗이올라왔습니다!!!

아디다스(Adidas)가 영국 프리미어리그 축구팀 아스널(Arsenal)의 유니폼 스폰에 합의했습니다!!!!

유니폼은 2018/19 시즌부터 볼 수 있을 예정입니다. 

 보도에 따르면,  현재 아스널이 이용하고 있는 퓨마와의 유니폼 스폰 계약은 연간 약 3000만파운드(410억원)에 계약 기간은 2019년 까지이고 아스널이 이날 아디다스와 새로운 계약을 맺음으로써, 앞으로 아스널은 새로운 계약에 따라 연간 약 9000만파운드(1250억원)를 받을 것이며, 이는  세계 두번째로 높은 금액입니다. (최고는 바르셀로나와 나이키) 


1986년 ~ 1994년까지 아스날과 아디다스는 함께했었습니다. 

그래서 제가 활동하는 커뮤니티에 좋은글을 가져왔습니다. 

페이스북 그룹 : 아스날 서포터즈 :: The Gunners


0123456789101112131415161718192021222324252627


블로그 이미지

벵거빠돌이

,

opos visual studio 적용....

C# 2018. 10. 15. 18:10


 http://monroecs.com/oposccos.htm


패키지 다운


관리자권한 커맨드창

Install the complete CCO package without user prompts by running 

this command in administrator mode:
    msiexec /passive /i OPOS_CCOs_1.xx.xxx.msi


도구 - 도구상자항목 선택 - COM구성요소에서 찾아서 체크


재부팅


하.....

블로그 이미지

벵거빠돌이

,

2018.10.07 오후 20:00 (KR) Craven Cottage






 

 Match Stats

 

Fulham

 

Arsenal

48.6

점유율% 

51.4

 21

 슛팅

9

4

 유효슛팅

7

 470

 패스시도

506

22

 클리어런스

17

4

 코너킥

2

4

 오프사이드

1

11

 파울

12



이날 롭 홀딩은 아스날 소속으로 50번째 출장했습니다. 




아스날은 이번 시즌 리그에서 터트린 19골 중 14골을 후반전에 득점했습니다.


프리미어 리그 역사상 단일 경기에 교체 투입된 두 선수(램지, 오바메양)가 

어시-득점을 기록한 것은 이번이 처음입니다.

블로그 이미지

벵거빠돌이

,

2018.10.05 오전 01:55 (KR) Baku Olympic Stadium




 

 Match Stats

 

Qarabagh

 

Arsenal

46

점유율% 

54

 9

 슛팅

9

4

 유효슛팅

4

 470

 패스시도

507


 클리어런스


6

 코너킥

4

6

 오프사이드

1

9

 파울

9




지난시즌 시작부터 수비수들중 나초보다 골을 더넣은사람은 없습니다!

블로그 이미지

벵거빠돌이

,




i - 현재 커서 위치에 Insert 하기
I - 현재 줄 맨앞에 Insert 하기
a - 현재 커서 다음칸에 Insert 하기
A - 현재 줄 맨뒤에 Insert 하기
O - 윗줄에 Insert 하기
o - 아랫줄에 Insert 하기

w - 단어 첫글자로 이동하기
W - 화이트스페이스 단위로 다음 글자로 이동하기
b - 백워드 방향으로 단어의 첫글자로 이동하기
B- 백워드 방향으로 화이트스페이스 단위로 다음 글자로 이동하기
e - 단어의 마지막 글자로 이동하기
ge - 백워드 방향으로 단어의 마지막 글자로 이동하기
gg - 문서 맨 앞으로 이동
G - 문서 맨끝으로 이동
^ - 문장 맨 앞으로 이동
0 - 라인 맨 앞으로 이동
$ - 문장 맨 뒤로 이동
f문자 - 문자의 위치로 이동 ; 를 누르면 계속 이동
F문자 - 백워드로 문자의 위치로 이동
t문자 - 문자의 앞위치로 이동 
T문자 - 백워드방향으로 문자의 앞위치로 이동

/단어 - 문서에서 단어 찾기 n이나 N으로 
다음/이전 찾기
* - 현재 단어를 포워드 방향으로 찾기
# - 현재 단어를 백워드 방향으로 찾기

Ctrl + f - 다음 페이지 이동
Ctrl + b - 이전 페이지 이동
Ctrl + u - 페이지절반만큼 다음으로 이동
Ctrl + d - 페이지절반만큼 이전으로 이동
H - 현재 화면의 맨 위라인으로 이동
M - 현재 화면의 중간 라인으로 이동
L - 현재 화면의 마지막 라인으로 이동

]] - 포워드 방향으로 여는 컬리 블레이스( { )로 이동
[[ - 백워드 방향으로 여는 컬리 블레이스( { )로 이동
][ - 포워드 방향으로 닫는 컬리 블레이스( { )로 이동
[] - 백워드 방향으로 닫는 컬리 블레이스( { )로 이동
% - {}나 ()에서 현재 괄호의 짝으로 이동

dd - 현재 줄 잘라내기
dw - 단어 잘라내기
yy - 현재 줄 복사하기
p - 붙혀넣기
r - 현재 글자 교체하기
u - Undo
Ctrl + R : Redo
x - 현재 글자 지우기
X - 앞의 글자 지우기
> - 들여쓰기
< - 내어쓰기
. - 이전 명령어를 다시 실행

v - 비쥬얼모드(비쥬얼 모드에서 커서 이동해서 
블럭지정 가능)
y - 복사하기
c - 잘라내기
cw - 단어 잘라내기
J - 다음 라인을 현재 줄의 끝으로 이어 붙힘
~ : 선택 문자 대소문자 변경
Ctrl + A : 숫자를 증가시키기
Ctrl + X : 숫자를 감소시키기

:w - 문서 저장하기
:q - 현재 문서 닫기
:q! - 저장하지 않고 닫기
:wq - 저장하고 닫기
:숫자 - 지정한 라인넘버로 이동

:new - 가로로 분할된 창 열기
:vs - 세로로 분할된 창 열기
Ctrl + w - 분할창 간에 이동하기
:tabnew - 새로운 탭 열기
:gt - 다음 탭으로 이동하기
:gT - 이전 탭으로 이동하기
:e ./ - 현재 탭에 오픈할 파일 탐색하기
( ./ 는 현재위치에서 탐색 시작)
:colorscheme 스키마명 - VIM의 칼라스키마를 변경함(blue, desert, evening 등.. 스키마명에서 탭누르면 자동완성됨)

zc - 코드 접기(fold)
zo - 접힌 코드 펼치기
zd - fold 지우기
zR - 접힌 코드 모두 펼치기
zM - 코드 모두 접기
zD - 모든 fold 지우기

:buffers - 현재 Vim에서 여러 파일을 열었을때 버퍼에 있는 목록 확인
:buffer 숫자 - 버퍼 목록에 나온 숫자를 입력하면 해당 파일을 오픈함 ( :buffer 대신 :b 도 가능)
:bnext - 버퍼에 있는 다음 파일로 이동 ( :bn 도 가능)
:bprevious - 버퍼에 있는 이전 파일로 이동 
:bp 도 가능)
:ball - 버퍼 목록에 있는 파일들이 가로로 분할된 창에 열림


블로그 이미지

벵거빠돌이

,

2018.09.29 오후 23:00 (KR) Emirates Stadium






 

 Match Stats

 

Arsenal

 

Watford

 63.4

점유율% 

36.6

 9

 슛팅

14

2

 유효슛팅

5

 541

 패스시도

299

 36

 클리어런스

22

6

 코너킥

6

1

 오프사이드

3

11

 파울

17


이름값했다 오늘은!

'Arsenal4Life' 카테고리의 다른 글

2018-19 EPL 8R 풀럼 vs 아스날  (0) 2018.10.08
UEL Match 2 카라바흐FK vs 아스날  (0) 2018.10.08
EFL 1Round 아스날 vs 브렌트포드  (1) 2018.10.01
EPL 6R 아스날 vs 에버튼  (0) 2018.09.28
UEL Match 1 Vorskla vs Arsenal  (0) 2018.09.23
블로그 이미지

벵거빠돌이

,

2018.09.27 오전 03:45 (KR) Emirates Stadium






 

 Match Stats

 

Arsenal

 

Brentford

56

점유율% 

44

 9

 슛팅

8

 6

 유효슛팅

5

 

 패스시도

11

 클리어런스

13

9

 코너킥

6

3

 오프사이드

2

10

 파울

6



웰벡은 이번시즌 프리미어리거중 처음 세개대회에서 득점했습니다. ㅋㅋㅋㅋㅋ

'Arsenal4Life' 카테고리의 다른 글

UEL Match 2 카라바흐FK vs 아스날  (0) 2018.10.08
EPL 7R 아스날 vs 왓포드  (0) 2018.10.02
EPL 6R 아스날 vs 에버튼  (0) 2018.09.28
UEL Match 1 Vorskla vs Arsenal  (0) 2018.09.23
EPL 5R 뉴캐슬Utd VS 아스날  (0) 2018.09.19
블로그 이미지

벵거빠돌이

,