Label 투명하게!

C# 2018. 12. 10. 11:30

PictureBox에 이미지 파일이 있는 경우였고,  (picTitle)

그 위에 Label을 올렸다.  (lblTitle)

투명하게를 위해 Color에 Transparent를 줬다. 

하지만,

투명은 커녕 라벨이 PictureBox의 이미지 파일을 가려버린다. 

그래서, 

폼에 해당하는 디자인코드를 수정했다. 

private void InitializeComponent()에 보면 해당 폼 이름에 해당하는 

(내 경우는  FrmInAmt)쪽 코드가 있다. 


this.Controls.Add(this.lblTitle);

를 지우고

this.Controls.Add(this.picTitle); 

그대로 두고

this.picTitle.Controls.Add(this.lblTitle);

추가!

했더니 투명하게 적용됬다. 끝


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

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

벵거빠돌이

,

해쉬테이블 (Hashtable)

C# 2018. 11. 8. 11:12

해쉬 테이블의 이해와 구현 (Hashtable)


조대협 (http://bcho.tistory.com)


기본적인 해쉬 테이블에 대한 이해


해쉬 테이블은 Key Value를 저장하는 데이타 구조로, value := get(key)에 대한 기능이 매우매우 빠르게 작동한다. 개발자라면 자주 쓰는 데이타 구조지만, 실제로 어떻게 작동하는지에 대해서 정확하게 알고 있지는 모르는 경우가 많다. 이 글에서는 해쉬 테이블에 대한 기본적인 구조와, 구현 방식에 대해서 설명 하도록 한다.

 

해쉬 테이블의 기본적인 개념은 다음과 같다.

이름을 키로, 전화 번호를 저장하는 해쉬 테이블 구조를 만든다고 하자.  전체 데이타 양을 16명이라고 가정하면

 

John Smith의 데이타를 저장할때, index = hash_function(John Smith) % 16  를 통해서 index 값을 구해내고, array[16] = John Smith의 전화 번호 521-8976”을 저장한다.



(출처 :https://en.wikipedia.org/wiki/Hash_table )

 

이런 형식으로 데이타를 저장하면, key에 대한 데이타를 찾을때, hash_function을 한번만 수행하면 array내에 저장된 index 위치를 찾아낼 수 있기 때문에, 데이타의 저장과 삭제가 매우매우 빠르다.

 

충돌 처리 방식에 따른 알고리즘 분류

 

그런데, 이 해쉬 테이블 문제는 근본적인 문제가 따르는데, hash_function(key) / size_of_array의 값이 중복이 될 수 가 있다.

 

예를 들어 저장하고자 하는 key가 정수라고 하고, hash_function key%10 이라고 하자. 그리고 size_of_array 10일때, key 1,11,21,31은 같은 index 값을 가지게 된다. 이를 collision (충돌)이라고 하는데, 이 충돌을 해결하는 방법에 따라서 여러가지 구현 방식이 존재한다.

 

1. Separate chaining 방식

 

JDK내부에서도 사용하고 있는 충돌 처리 방식인데, Linked List를 이용하는 방식이다.

index에 데이타를 저장하는 Linked list 에 대한 포인터를 가지는 방식이다.



(출처 :https://en.wikipedia.org/wiki/Hash_table )

 

만약에 동일  index로 인해서 충돌이 발생하면 그 index가 가리키고 있는 Linked list에 노드를 추가하여 값을 추가한다.  이렇게 함으로써 충돌이 발생하더라도 데이타를 삽입하는 데 문제가 없다.

 

데이타를 추출을 하고자할때는 key에 대한 index를 구한후, index가 가리키고 있는 Linked list를 선형 검색하여, 해당 key에 대한 데이타가 있는지를 검색하여 리턴하면 된다. 

삭제 처리


key를 삭제하는 것 역시 간단한데, key에 대한 index가 가리키고 있는 linked list에서, 그 노드를 삭제하면 된다.

Separate changing  방식은 Linked List 구조를 사용하고 있기 때문에, 추가할 수 있는 데이타 수의 제약이 작다.

 

참고 : 동일 index에 대해서 데이타를 저장하는 자료 구조는 Linked List 뿐 아니라, Tree를 이용하여 저장함으로써, select의 성능을 높일 수 있다. 실제로, JDK 1.8의 경우에는 index에 노드가 8개 이하일 경우에는 Linked List를 사용하며, 8개 이상으로 늘어날때는 Tree 구조로 데이타 저장 구조를 바꾸도록 되어 있다.

 

2. Open addressing 방식

 

Open addressing  방식은 index에 대한 충돌 처리에 대해서 Linked List와 같은 추가적인 메모리 공간을 사용하지 않고, hash table array의 빈공간을 사용하는 방법으로, Separate chaining 방식에 비해서 메모리를 덜 사용한다. Open addressing  방식도 여러가지 구현 방식이 있는데, 가장 간단한 Linear probing 방식을 살펴보자

 

Linear Probing

 

Linear Probing 방식은 index에 대해서 충돌이 발생했을 때, index 뒤에 있는 버킷중에 빈 버킷을 찾아서 데이타를 넣는 방식이다. 그림에서 key % 10 해쉬 함수를 사용하는  Hashtable이 있을때, 그림에서는 충돌이 발생하지 않았다.

 

 


아래 그림을 보자, 그런데, 여기에 11을 키로 하는 데이타를 그림과 같이 넣으면 1이 키인 데이타와 충돌이 발생한다. (이미 index1인 버킷에는 데이타가 들어가 있다.) Linear Probing에서는 아래 그림과 같이 충돌이 발생한 index (1) 뒤의 버킷에 빈 버킷이 있는지를 검색한다. 2번 버킷은 이미 index2인 값이 들어가 있고, 3번 버킷이 비어있기 3번에 값을 넣으면 된다.


검색을 할때, key 11에 대해서 검색을 하면, index1이기 때문에, array[1]에서 검색을 하는데, key가 일치하지 않기 때문에 뒤의 index를 검색해서 같은 키가 나오거나 또는 Key가 없을때 까지 검색을 진행한다. 


삭제  처리


이러한 Open Addressing의 단점은 삭제가 어렵다는 것인데, 삭제를 했을 경우 충돌에 의해서 뒤로 저장된 데이타는 검색이 안될 수 있다. 아래에서 좌측 그림을 보자,  2index를 삭제했을때, key 11에 대해서 검색하면, index1이기 때문에 1부터 검색을 시작하지만 앞에서 2index가 삭제되었기 때문에, 2 index까지만 검색이 진행되고 정작 데이타가 들어 있는 3index까지 검색이 진행되지 않는다.

그래서 이런 문제를 방지하기 위해서 우측과 같이 데이타를 삭제한 후에, Dummy node를 삽입한다. Dummy node는 실제 값을 가지지 않지만, 검색할때 다음 Index까지 검색을 연결해주는 역할을 한다.


Linear probing에 대한 샘플 코드는

http://www.cs.rmit.edu.au/online/blackboard/chapter/05/documents/contribute/chapter/05/linear-probing.html

http://www.tutorialspoint.com/data_structures_algorithms/hash_data_structure.htm

를 참고하기 바란다.

Dummy Node를 이용해서 삭제를 할때, 삭제가 빈번하게 발생을 하면, 실제 데이타가 없더라도 검색을 할때, Dummy Node에 의해서, 많은 Bucket을 연속적으로 검색을 해야 하기 때문에, Dummy Node의 개수가 일정 수를 넘었을때는 새로운 array를 만들어서, 다시 hash를 리빌딩 함으로써, Dummy Node를 주기적으로 없애 줘야 성능을 유지할 수 있다.


Resizing

Open addressing의 경우, 고정 크기 배열을 사용하기 때문에 데이타를 더 넣기 위해서는 배열을 확장해야 한다. 또한 Separate changing에 경우에도 버킷이 일정 수준으로 차 버리면 각 버킷에 연결되어 있는 List의 길이가 늘어나기 때문에, 검색 성능이 떨어지기 때문에 버킷의 개수를 늘려줘야 한다. 이를 Resizing이라고 하는데, Resizing은 별다른 기법이 없다. 더 큰 버킷을 가지는 array를 새로 만든 다음에, 다시 새로운 arrayhash를 다시 계산해서 복사해줘야 한다.


해쉬 함수


해쉬 테이블 데이타 구조에서 중요한 것중 하나가 해쉬 함수(hash function)인데, 좋은 해쉬 함수란, 데이타를 되도록이면 고르게 분포하여, 충돌을 최소화할 수 있는 함수이다. 수학적으로 증명된 여러가지 함수가 있겠지만, 간단하게 사용할 수 있는 함수를 하나 소개하고자 한다. 배경에 대해서는 http://d2.naver.com/helloworld/831311 에 잘 설명되어 있으니 참고하기 바란다. (필요하면 그냥 외워서 쓰자)

String key

char[] ch = key.toChar();

int hash = 0;

for(int i=0;i<key.length;i++)

 hash = hash*31 + char[i]

 

Cache를 이용한 성능 향상


해쉬테이블에 대한 성능 향상 방법은 여러가지가 있지만, 눈에 띄는 쉬운 방식이 하나 있어서 마지막으로 간략하게 짚고 넘어가자. 해쉬테이블의 get(key)put(key)에 간단하게, 캐쉬 로직을 추가하게 되면, 자주 hit 하는 데이타에 대해서 바로 데이타를 찾게 함으로써, 성능을 간단하게 향상 시킬 수 있다.  




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

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

벵거빠돌이

,

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);  


블로그 이미지

벵거빠돌이

,

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구성요소에서 찾아서 체크


재부팅


하.....

블로그 이미지

벵거빠돌이

,
public partial class BitTest : Form
{
        const char STX = (char)0x02;
        const char ETX = (char)0x03; //End Text [응답용Asc]
        const char EOT = (char)0x04; //End of Text[요구용 Asc]
        const char ENQ = (char)0x05; //Enquire[프레임시작코드]
        const char ACK = (char)0x06; //Acknowledge[응답 시작]
        const char NAK = (char)0x15; //Not Acknoledge[에러응답시작]
        private SerialPort m_serialPort;
    
        public BitTest()
        {
            InitializeComponent();
        }
 
        private void WordReadTest_Load(object sender, EventArgs e)
        {
            SerialPortOpen();
        }
 
        private void WordReadTest_FormClosing(object sender, FormClosingEventArgs e)
        {
            SerialPortClose();
        }
        private void btnOpen_Click(object sender, EventArgs e)
        {
            SerialPortOpen();
        }
 
        private void btnClose_Click(object sender, EventArgs e)
        {
            SerialPortClose();
        }
 
        /// 
        /// 포트오픈
        /// 
        private void SerialPortOpen()
        {
            m_serialPort = new SerialPort();
            m_serialPort.PortName = "Com7";
            m_serialPort.BaudRate = 19200;
            m_serialPort.Parity = Parity.None;
            m_serialPort.DataBits = 8;
            m_serialPort.StopBits = StopBits.One;
            m_serialPort.Handshake = Handshake.None;
            m_serialPort.ReadTimeout = 500;
            m_serialPort.WriteTimeout = 500;
            m_serialPort.Open();
 
            btnOpen.Enabled = false;
            btnClose.Enabled = true;
            btnExecute.Enabled = true;
        }
        /// 
        /// 포트 종료
        /// 
        private void SerialPortClose()
        {
            m_serialPort.Close();
            btnOpen.Enabled = true;
            btnClose.Enabled = false;
            btnExecute.Enabled = false;
        }
 
        //실행
        private void btnExecute_Click(object sender, EventArgs e)
        {
            DateTime t1 = DateTime.Now;
            string strOutputData = ENQ + "00RSS01" + "07%MX0000" + EOT;
            m_serialPort.Write(strOutputData);
            lblOutputData.Text = strOutputData;
            do
            {
                //데이타를 전부 PLC로 전송 하기 위함..
            } while (m_serialPort.WriteBufferSize == 0);
            string indata= DataRead();
            TimeSpan span = DateTime.Now.Subtract(t1);
            lblMillisecond.Text = span.Milliseconds.ToString();
        }
 
        /// 
        /// PLC로 부터 수신된 데이타를 가지고 온다
        /// 
        /// 
        private string DataRead()
        {
            bool m_Next = false;
            string strInData = string.Empty;
            string strRetValue= string.Empty;
        
            DateTime start = DateTime.Now;
            do
            {
                string msg = m_serialPort.ReadExisting();
                strInData += msg;
                //TODO : 데이타에 종료문자가 있으면...
                if (msg.IndexOf(ETX) > 0)
                {
                    //TODO 데이타 처음에 정상 응답이 있으면
                    if (strInData[0] == ACK)
                    {
                        //TODO 들어오는 데이타를 분석..[ETX(1)+국번(2)+비트읽기(3)+블륵수(2)]
                        lblInputData.Text = strInData;//Test용
                        strRetValue = strInData.Substring(8, strInData.Length - 9); //실제Data
                        m_Next = true;
                    }
                    //TODO: 데이타에 비정상 응답이 들어오면..
                    else if (strInData[0] == NAK)
                    {
                        lblInputData.Text = "NAK";
                        strRetValue = "-1";
                        m_Next = true;
                    }
                }
                //DOTO : 응답이 없으면 0.5초간은 로프를둘면서 기다란다.
                TimeSpan ts = DateTime.Now.Subtract(start);
                if (ts.Milliseconds > 500)
                {
                    lblInputData.Text = "TimeOut";
                    strRetValue = "-3";
                    m_Next = true;
                }
            } while (!m_Next);
            return strRetValue;
        }
    }
}

블로그 이미지

벵거빠돌이

,
첫번째 방법 1. 부모창에서 자식창을 선언.
private void button3_Click(object sender, EventArgs e)
{
     Form2 chfm = new Form2();
     chfm.ShowDialog(this);
}
2. 자식창에서 부모값으로 넘김
private void button1_Click(object sender, EventArgs e)
{
    ((Form1)(this.Owner)).temp = textBox1.Text;
}
두번째 방법. 1. 자식창에서 부모와 같은 클래스로 선언한다.
public Form2()
{
    InitializeComponent();
}
public Form1 fm;
2. 부모창에서 자식창을 선언할 때 자식클래스에 선언한 부모와 같은 클래스형에 참조시킨다.
private void button3_Click(object sender, EventArgs e)
{
    Form2 chfm = new Form2();
    chfm.fm = this;
    chfm.ShowDialog();
}
3. 자식창에서 다음과 같이 제어한다.
private void button1_Click(object sender, EventArgs e)
{
    fm.temp = textBox1.Text;
}

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

c#에서 비관리코드를 호출하는 방법  (0) 2018.10.24
[MFC] LPSTR,LPCSTR,LPCTSTR .. 과연 무엇인가?  (0) 2018.10.23
opos visual studio 적용....  (0) 2018.10.15
C#으로 LS PLC와 RS232C 통신하기  (0) 2018.04.05
DataGridView 셀접근  (0) 2018.03.27
블로그 이미지

벵거빠돌이

,

DataGridView 셀접근

C# 2018. 3. 27. 15:04
		
for (int i=1; i<3; i++)
		{
			gridSale.Rows.Add(sRet[i]);
			gridSale.Rows.Insert(1, sRet[1]);
			gridSale.Rows.Add(sRet[i]);
			gridSale[1, i].Value = sRet[i];
			gridSale.Rows.Insert(iRowCnt, iRowCnt+1, sRet[1], Ret[2]);
			gridSale.Rows[iRowCnt].Cells["PLU_NM"].Value = sRet[1];
		}
블로그 이미지

벵거빠돌이

,