Q&A

  • 고지범님...다시 한 번 부탁드립니다.
님 덕분에 한 가지 더 배웠습니다. 감사한 마음을 어떻게 전해 드려야할지.ㅋㅋ
마음은 음료수라두 한 잔 사드리구 싶은데..

어제 서스를 보고 직접코딩을 해보았습니다. 참 많이 도움이 되었구요.
그중에 이해 안되는 부분이 있어서 요약 해봤습니다.

1.코드중에서 ErrordDllGetVersion 함수가 이해가 안되어서요. 요 함수를 보면
  stdcall이라구 붙여주었는데 이것두 dll함수인가요? dll함수라면 동적으로
해야되지 않나요? 그리구

2.DllGetVersion := ErroredDllGetVersion 에서 ErroredDllGetVersion 함수를
호출할 때 인자값은 넣어 주지 않아도 에러가 발생 안하던데 왜그런가요?

3.ErroredDllGetVersion함수는 호출되어도 실행되지 않던데 왜그런가요?

4.MessageBox(0,pchar('dllGetVersion 함수 없습니다'),pchar('Error'),MB_OK)
이 메시지는 함수가 호출되면 자동으로 동작하는데 조건을 걸어서 만족하면 실행되어야 하는것은 아닌지요? 예를 들어 GetProcAddress에서 포인터주소가 없을 때라든지.

5.그리구 pchar 요거는 붙여주어야하나요? 에러나던데요. 없으면.....

감사하구요. 제가 보답한 기회가 있다면 말씀해주세요..
3  COMMENTS
  • Profile
    고지범 2002.09.04 22:50
    ^_____^
    호접의 답변으로 기쁘셨다니 호접의 한사람으로써 기쁜 맘을... ^^

    1.코드중에서 ErrordDllGetVersion 함수가 이해가 안되어서요. 요 함수를 보면
      stdcall이라구 붙여주었는데 이것두 dll함수인가요? dll함수라면 동적으로
    해야되지 않나요? 그리구
    1. 답.
    제가 올린 예제 코드는 함수 포인터를 사용하는 겁니다.
    함수 포인터와 똑같은 형식으로 에러함수를 구현 한거죠.
    이건 DLL코드가 아니라 DLL코드가 없거나, DLL이 없거나 등... 에러의 상황에서 설정되는 코드입니다.

    2.DllGetVersion := ErroredDllGetVersion 에서 ErroredDllGetVersion 함수를
    호출할 때 인자값은 넣어 주지 않아도 에러가 발생 안하던데 왜그런가요?
    2. 답.
    위 부분은 함수를 호출하는 것이 아니라 함수 포인터를 설정하는 부분입니다. 따라서 에러 발생이 없습니다. 오히려 위의 경우
    DllGetVersion := ErroredDllGetVersion(....); 는 에러 죠... ErroredDllGetVersion의 리턴 값은 HRESULT인 데 담는 놈이 포인터니깐..

    3.ErroredDllGetVersion함수는 호출되어도 실행되지 않던데 왜그런가요?
    3. 답.
    예제를 그대로 돌리셨으면 초기에 ErroredDllGetVersion이 설정되고 난 다음 DLL함수로 치환이 되어서 그럴 겁니다. LoadLibrary 부분에서 dll 이름을 엉뚱한 놈으로 변경시키면 당연히... ErroredDllGetVersion 가 동작하게 되죠.

    4.MessageBox(0,pchar('dllGetVersion 함수 없습니다'),pchar('Error'),MB_OK)
    이 메시지는 함수가 호출되면 자동으로 동작하는데 조건을 걸어서 만족하면 실행되어야 하는것은 아닌지요? 예를 들어 GetProcAddress에서 포인터주소가 없을 때라든지.
    4. 답.
    그렇죠... 게을러서 한 경우로만 코딩한 겁니다...^^

    5.그리구 pchar 요거는 붙여주어야하나요? 에러나던데요. 없으면.....
    5. 답.
    아마 델파이 5아니신가요? 암튼 MessgeBox 호출 형식이 PChar를 요구합니다.  

    헉헉헉.. 출장 떠나기 5분전에 부랴부랴 써서리... 주절주절이군요...
    다다음주에나 다시 들어오게 되겠네요...
    오늘도 즐입니다.
  • Profile
    이원상 2002.09.05 02:12
    정말 기쁘군요. 이렇게 설명해주시는 분이 계시다니 저에게는 기쁜일입니다
    그래서 확실히 알려구 다시 질문드립니다.


    DllGetVersion에 관하여
    1.2.DllGetVersion : TDllGetVersion; 이렇게 선언하면
    DllGetVersion 은 function(a_PassedPointer :PDLLVERSIONINFO):HRESULT;stdcall; 이라는 말 맞나요? 그러면
    function(a_PassedPointer :PDLLVERSIONINFO):HRESULT;stdcall = ErroredGetVersion 이렇게 플이 되는거 아닌가요? 포인터를 받을 수 있을까요?

    ErroredDllGetVersion에 관하여
    1.2에서 포인터를 설정하여 주신다구 하신 말씀이 이해가 안됩니다.
    ErroredDllGetVersion이라는 함수명 자체가 포인터의 주소를 가지고 있다고 보면 되나요?
    예) var
                    pointervalue : pointer;
            pointervalue := ErroredDllGetVersion 이렇게도 치환이 가능하겠내요.

    3.ErroredDllGetVersion 끝부분에 stdcall를 붙이므로서 외부에서 함수를 가져왔다는 표현이라는 말씀인가요? 그래서 unit common 내부에서 찾지 않는다는
    말씀인가요?

    감사합니다.
  • Profile
    고지범 2002.09.05 09:08
    요기는 춘천입다.. 출장나온 사람들과 스타하러 왔다가서리... ^____^

    1. function pointer
    가령 다음과 같이 선언하였다고 하면...
    type
       TMyFunc = function(a_Bool:Boolean):Cardinal;stdcall;
          1)             2)             3)                  4)           5)
    1) 자료형 선언. TMyFunc라는 것은 function pointer data type을 나타냄.
    2) function pointer 세부 설명. 즉, TMyFunc 라는 것은 인자를 Boolean 변수 1개를 받아서 Cardinal을 리턴하는 함수에 대한 포인터임을 명시하는것.
    3) Argument list
    4) 리턴 형식
    5) Calling convension.
    calling convension에 대해서 조금만 자세히.. 많이들 아시겠지만 혹시나 모르시는 분들을 위해서리....
    function/procedure 호출이 일어나는 것은 내부적으로 stack을 사용합니다. 즉 현재 진행 포인터(IP : Instruction Pointer) 넣어두고 각 인자들을 넣어두고 함수를 호출하면 호출된 함수는 인자를 stack에서 꺼내서 처리하고 실행이 끝나면 stack에 들어있던 IP로 복귀하죠(물론 스텍에 집어넣어질 때 IP 값이 증가되어서 들어가기 때문에 복귀 주소는 함수호출 바로 다음 라인으로 되죠..). stack을 사용하는 것은 동일하지만 몇가지 다른 게 있으니,
    첫째) 누가 스택을 만들어서 나중에 사용이 끝나면 파괴시키는가?
    둘째) 스택에 인자가 들어갈 때 어떠한 순서로 들어가는가?
    stdcall / cdecl / safecall / pascal 등이 이에 해당합니다. 각 예약어들이 어떻게 동작하는 지는 헬푸를 참조하시고 (사실 다 외우지 못해서... 걍 필요할 때마다 찾기 땜시... 헬푸 인덱스는 calling convension으로 하심됨다.)

    따라서 stdcall을 붙인다고 해서 그것이 dll 함수 이거나 외부 함수라는 소리는 아님다.. 단지 호출 규약이 다른 형태라는 것을 알려주는 것일 뿐이죠.... 아... 물론 호출규약(calling convension)은 전역함수에만 해당됩니다. 멤버함수는 아마 모두 stdcall일 껄요? (not sure... ^^)

    글구 함수 포인터에 관해서...
    C++을 좀 도용해서 설명을 하죠...
    C++에서 함수가 실행된다면....
    int nIndex;
    nIndex = GetIndex(25);
    위 문장을 생각해보면... C++ 적으로는 다음과 같습다.

    nIndex =                // 리턴 값을 설정하라는 소리고,
                GetIndex   // 함수 포인터가 GetIndex라는 소리고
                (25);         // () 가 함수 실행 연산자로 처리가 됩니다.
    따라서 위의 코드를 사람의 언어로 바꾸어서 말을 하면....
    GetIndex 라는 함수 포인터가 가리키는 주소에 실제 원하는 함수의 시작점이 있으니 25 라는 인자 값으로 이를 실행시켜서 그 결과 값을 nIndex 변수어 넣어라... 라는 뜻입다.

    메모리  내용     메모리  내용
            |                     |  
            |             20000 |  실제 GetIndex함수가 실행되는 코드
            |                     |  
    100    | 20000            |  
            |                     |  
    이런 경우  
    100 = GetIndex 라는 뜻이고,
    GetIndex(25) 는 20000 번지에 있는 내용을 기계어 명령으로 하여 실행시키되 인자값을 25 주어서 실행하라는 뜻이 된다는 것이죠.
    물론 C/C++ 내용이지만 델파이도 동일합니다. (제가 보기엔...)

    따라서
    var
       tempPointer :TDllGetVersion;
    begin
       tempPointer := ErroredDllGetVersion;
    이라는 코드는 맞는 형식입니다.
       tempPointer        :=          ErroredDllGetVersion;
       L-value                           R-Value
       요구하는 형식은                데이터 타입 : 함수 포인터
       함수 포인터 -                     - 4byte Cardinal;
       4byte Cardinal
    하지만
       tempPointer := ErroredDllGetVersion(); 이 것은 틀리죠.
       tempPointer        :=          ErroredDllGetVersion();
       L-value                           R-Value
       요구하는 형식은                실행 결과 값
       함수포인터 -                    HResult - 4byte Cardinal;
       4byte Cardinal                  하지만 타입이 달라서 못한다는 컴파일러 에러
    위 문장과 아래 문장이 서로 다르죠?

    # 참고... 함수 포인터와 포인터의 차이.
    델6에 보시면 TAssertProc 이라는 함수 포인터가 있습다. 델파이 메거진 7월호 보심 디버깅 테크닉 부분에서 설명이 나오는 데요. 걍 차이점은...
    사용하는 형식에 @가 있느냐 하는 겁다.
    type
       TDllGetVersion = function (a_Pointer:PDllVersionInfo):HRESULT;stdcall;
    var
        DllGetVersion : TDllGetVersion;
        tempPointer   : Pointer;

    procedure MyDllGetVersion(a_Pointer:PDllVersionInfo):HRESULT;stdcall;
    begin
    ....
    end;

    procedure TForm1.Button1Click(Sender:TObject);
    begin
       // 요로코롬 사용해야 정답임다...
       DllGetVersion := MyDllGetVersion;
       tempPointer   := @MyDllGetVersion;
    end;

    # 참고 2.
    calling convension을 보신 분들은 혹시나 모르신다면 name-mangling 부분도 함 같이 찾아 보세요... C에서 EXTERN 'C' {} 부분에 관련 되는 것이죠.
    DLL 만들 때 왜 C++ 을 사용하면 .DEF를 같이 만들어야 하는 가에 관한 부분입니다. 델파이의 경우 name-mangling은 EXTERN 'C' {} 와 동일합니다.
    (제가 알기로는 name-mangling 부분에 대한 야그가 없는 것으로 아는 데... 이 부분이 델파이에서 어떻게 구현되는 지 아시는 분은 나중에 리플 주시길...)

    # 나가며...
    헉헉헉... 대학교 2학년 쯤 나오는 내용이었던 거 같은 데... 학부때 말고 이렇게 장문으로 써보긴 처음인 듯...
    혹시나 여기 까지 읽으신 분들... 정말 고생하셨습다...
    이렇게 까지 길게 쓰지 않으려 했는 데 넘...길어졌군요.. 지송할 따름입다...
    그럼 하시는 일 모두 잘 되시고... 오늘도 즐입다.