C#에서 DLL만들고 사용하기

개발환경은 Visual C# 2010 Express에서의 예제이다.
이 내용은 Visual Studio 2010에서 해도 문제가 없다.

메뉴 표시는 대신 한글이여서 조금은 이해하기 편할 것 같다.


먼저 DLL을 사용할 프로젝트를 만든다.
먼저 File > New Project를 선택한다.

Console Application을 선택하고 적당한 이름을 입력한 후 OK를 누른다.
여기서 입력한 이름으로 프로젝트가 만들어지고 이 이름의 네임스페이스가 생긴다.

그러면 아래와 같이 자동으로 소스가 조금 만들어진다.


사용할 DLL 프로젝트 만들기

솔루션에서 왼쪽 클릭을 하고 Add > New Project를 선택한다.



Class Library를 선택하고 DLL의 이름을 적고 OK를 누른다.


DLL에 대한 내용을 적어둔다.



using System;

namespace MyCompo
{
    public class Calcurate
    {
        public int Add(int a, int b)
        {
            return (a + b);
        }
    }
}


DLL을 Build한다.


작성된 DLL을 참조하기 위해서는 DLL을 사용하려는 프로젝트의 References(참조)에서 오른쪽 클릭을 하여 Add Reference를 선택한다.


그리고 Projects 탭을 선택하면 지금 작성한 DLL의 프로젝트를 선택하고 OK를 누른다.

정상적으로 Dll이 참조되면 References 밑에 추가한 DLL의 이름이 표시된다.

그리고 아래와 같이 코드를 작성한다.


using System;
// DLL Namespace 추가
using MyCompo;

namespace UseDll
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 10;
            int b = 20;
            int c = 0;

            // DLL에 정의된 클래스로 객체 정의하기
            Calcurate cal = new Calcurate();

            // DLL의 메서드 사용하기
            c = cal.Add(a,b);

            Console.WriteLine("{0} + {1} = {2}", a, b, c);
            Console.ReadLine(); 
        }
    }
}

그리고 실행하면 아래와 같은 결과가 표시된다.




Posted by 떡잎

DLL의 파라미터를 동적 배열로 넘기기
DLL의 반환값을 동적 배열로 반환하기

library SecondDll;

uses
  SysUtils,  Classes;

{$R *.res}

type
  TDynArrVal = array of Integer;

function DynamicSum(DynArr: TDynArrVal): Integer; stdcall;
var
  i, iSum : integer;
begin
  iSum := 0;

  for i := Low(DynArr) to High(DynArr) do
    iSum := iSum + DynArr[i];

  Result := iSum;
end;

function DynamicReturn(iCnt: integer): TDynArrVal; stdcall;
var
  i : integer;
  DynArr : TDynArrVal;
begin
  SetLength(DynArr, iCnt);

  for i := 0 to iCnt - 1 do
    DynArr[i] := i + 1;

  Result := DynArr;
end;

exports
  DynamicSum,
  DynamicReturn;

begin
end.

DLL의 파라미터를 동적 배열로 넘기기
DLL의 반환값을 동적 배열로 반환하는 DLL 사용하기

unit uMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    // 동적 배열형 선언
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

type
  TDynArrVal = array of Integer;

function DynamicSum(DynArr: TDynArrVal): Integer; stdcall; external 'SecondDll.dll';
function DynamicReturn(iCnt: integer): TDynArrVal; stdcall; external 'SecondDll.dll';


implementation

{$R *.dfm}


procedure TForm2.Button1Click(Sender: TObject);
var
  DynArrTest : TDynArrVal;
  i : integer;
begin
  // 동적 배열 크기설정
  SetLength(DynArrTest, 10);

  for i := Low(DynArrTest) to High(DynArrTest) do
    DynArrTest[i] := i + 1;

  // 동적 배열 파라미터 함수 호출
  Button1.Caption := IntToStr(DynamicSum(DynArrTest));


end;

procedure TForm2.Button2Click(Sender: TObject);
var
  DynArrTest : TDynArrVal;
  i, iSum  : integer;
begin
  iSum := 0;

  SetLength(DynArrTest, 10);
  DynArrTest := DynamicReturn(10);

  for i := Low(DynArrTest) to High(DynArrTest) do
    iSum := iSum + DynArrTest[i];

  Button2.Caption := IntToStr(iSum);
end;
end.





SecondDll.zip

예제 소스


 

Posted by 띠리


델파이 DLL 만들기

library FirstDll;

uses
  SysUtils,  Classes,  Windows;

{$R *.res}

// 더하기
function NumPlus(iA, iB: Integer): Integer; stdcall;
begin
  Result := iA + iB;
end;

// 메세지 박스 표시하기
function MsgBoxYN(pcMsg: PChar; pcTitle: PChar): BOOL; stdcall;
begin
  Result := (MessageBox(0, pcMsg, pcTitle, MB_YESNO or MB_ICONQUESTION) = IDYES);
end;

exports
  NumPlus,
  MsgBoxYN;

begin
end.

델파이에서 만든 DLL  사용하기

unit uMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm2 = class(TForm)
    edtNumA: TEdit;
    edtNumB: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Button1: TButton;
    edtTitle: TEdit;
    edtMsg: TEdit;
    Label3: TLabel;
    Label4: TLabel;
    Button2: TButton;
    procedure Button2Click(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

// DLL 함수 선언
function NumPlus(iA, iB: Integer): Integer; stdcall; external 'FirstDll.dll';
function MsgBoxYN(pcMsg: PChar; pcTitle: PChar): BOOL; stdcall; external 'FirstDll.dll';

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
var
  iRet : integer;
begin
  iRet := NumPlus(StrToInt(edtNumA.Text), StrToInt(edtNumA.Text));
  Button1.Caption := IntToStr(iRet);
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  MsgBoxYN(PChar(edtTitle.Text), PChar(edtMsg.Text));
end;

end.


FirstDll.zip

예제 소스



Posted by 띠리
델파이에서 Form이 있는 DLL만들기

먼저 File메뉴에서 Other를 선택한다.
사용자 삽입 이미지

New Items에서 DLL Wizard를 선택한다.

사용자 삽입 이미지

그리고 만들어진 DLL 프로젝트에서 오른쪽 클릭을 하여 Add New의 Form을 선택한다.

사용자 삽입 이미지

이렇게 하면 DLL에 폼을 추가하게 된다.
추가한 폼에 컨트롤들을 추가하고 이 폼에 대한 코딩을 한다.

사용자 삽입 이미지

폼 DLL을 확인하기 위한 프로그램을 만들기 위해서
ProjectGroup에서 오른쪽 클릭을 하여 Add New Project를 선택한다.

사용자 삽입 이미지

New Items에서 VCL Forms Application을 선택한다.

사용자 삽입 이미지


이렇게 작업하면 우선 폼이 있는 DLL을 테스트할 수 있는 환경이 갖추어진다.
(이 글을 전혀 델파이를 잘 모르는 사람을 위해 썼음)

사용자 삽입 이미지

DLL이니까 당연히 DLL에서 사용할 함수를 작성해야된다.
파일이름은 어떻게 적던 상관없다.
설명은 위 이미지의 파일명으로 설명한다.

폼DLL에 컨트롤을 추가하려면 uDllFrm.dfm을 더블클릭하면 컨트롤을 추가하거나
소스를 변경한다.

uDllFrm.pas(DLL 소스)에 DLL 함수를 추가한다.

//------------------------------------------------------------------------------
//    DLL 함수
//------------------------------------------------------------------------------
function GetDlgData(hOwner:HWND):string;stdcall;

exports
  GetDllDlgData;

implementation

{$R *.dfm}

function GetDlgData(hOwner:HWND):string;stdcall;
begin
  Application.Handle := hOwner;

  if frmDlg = nil then
    frmDlg := TfrmDlg.Create(application);

  frmDlg.ShowModal;

  result := frmDlg.btnTest.Caption;
  FreeAndNil(frmDlg);
end;



uDllDlgTest.pas(DLL 사용 소스)를 더블클릭하여 DLL함수를 선언한다.

var
  frmMain: TfrmMain;

implementation

//------------------------------------------------------------------------------
//    DLL 함수
//------------------------------------------------------------------------------

function GetDlgData(hOwner:HWND):string;stdcall; external 'dllDlg.dll';

{$R *.dfm}

procedure TfrmMain.btnTestClick(Sender: TObject);
begin
   pnlTest.Caption := GetDlgData(Application.Handle);
end;


참고 샘플
invalid-file

델파이 2006용

Posted by 띠리

델파이에서 VC 8.0(VS 2005)에서 작성한 DLL을 사용하는데
개발환경이 셋업되어있지않은 곳에서 사용하는데 문제가 있어서 몇일간 삽질한 내용을
정리하고자한다.

테스트 환경
  우선 Visual C++ Ver 8.0에서 DLL을 만들었다.
  그리고 그것을 Delphi 2005에서 만든 실행 파일에서 불러서 쓰려했다.

  실행파일을 실행한 OS는 Windows XP Professional SP2로
  개발환경이 전혀 깔려있지않은 상태였다.
 


첫번째 삽질
델파이라 만든 실행파일을 실행하면 아래의 메세지가 떴다.
"응용 프로그램을 제대로 초기화하지 못했습니다(0xc0150002)."

→ 문제는 Visual C++ Ver 8.0에서 DLL을 만들 때 Debug 모드로 빌드를 해서
    Debug용 DLL로 테스트를 했었다.
    Release 모드로 바꾸어서 빌드 해봤지만 에러는 뜨지 않았지만
    dll이 제대로 동작하지 않았다.

두번째 삽질
VC 8.0(VS 2005)으로 DLL을 만들면 DLL만 카피한다고 DLL이 제대로 동작하지 않는다.
자세한 내용은 옆의 링크 참조 배포(C++)
여하튼 가장 편한 방법은 아래의 패키지를 설치하는 것이 가장 편한 것같다.
Microsoft Visual C++ 2005 재배포 가능 패키지(x86)
(무엇이 좋아졌는지는 잘 모르겠지만 왜 더 불편하게 만들어졌는지가 잘 이해가 안간다.)

세번째 삽질
호출 규칙이란 것을 모르고 DLL을 작성하였다.
(아직도 제대로 이해하지 못하고 있지만...)
Visual C++ 2005의 호출규칙(컴파일 옵션) 에는 아래와 같은 것들이 있다.
    __cdecl
    __fastcall
    __stdcall  

__cdecl, __pascal, __stdcall의 차이점 (구글검색)
위 링크를 보면 자세한 내용 있는 홈페이지를 볼 수 있다.
(주로 한 글이 계속 펌되어져있다.)

가변매개인자를 꼭 사용해야만 한다면 반드시 cdecl을 사용해야된다고 한다.
이런 위의 사실들을 모르고 dll을 만들었기 때문에 제대로 dll을 인식하지 못했다.

Visual C++ 2005의 호출규칙 컴파일 옵션을 설정하는 방법은
메뉴의 프로젝트(P)에서 속성(P)[Alt+F7]을 선택하면 속성 페이지가 선택된다.

구성에서 활성(Release)를 선택하고
구성 속성 밑의 C/C++를 선택하고 그 밑의 고급을 선택한다.
그러면 호출규칙이 표시된다.

물론 델파이에서 DLL 함수 선언할 때도 Visual C++ 2005에서 선택한
호출 규칙과 동일하게 선언해야 된다.

이런 것들을 제대로 파악을 못해서 몇일동안 정말 삽질을 했다.
왜 안돌아갈까 왜 안돌아갈까?
많이 고민했다.

시간이 지나서 또 삽질할까봐 적어둔다.

Posted by 띠리


문자열 넘기고 받는 간단한 C의 DLL 소스

char    szTemp[50];


extern "C"

{

    // DLL 문자열 인수 입력

    BOOL PASCAL EXPORT SetStr(char *pszSend)

    {

        sprintf(szTemp, "%s", pszSend);

        return true;

    }


    // DLL 문자열 인수로 문자열 넘겨주기

    BOOL PASCAL EXPORT GetStr(char *pszSend)

    {

        sprintf(pszSend, "%s + %s", szTemp , szTemp);

        return true;

    }


}



DLL에서 문자열을 주고 받기 위해서는
델파이에서 PChar형으로 문자를 DLL에 넘겨주고 받으면 된다.


문자열을 넘기고 받는 델파이 소스

unit dtest;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, TeEngine, Series, TeeProcs, Chart;

type
  TfrmMain = class(TForm)
    btnTest: TButton;
    edtStr: TEdit;
    lblStr: TLabel;
    edtNo: TEdit;
    lblNo: TLabel;
    procedure btnTestClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

// DLL에서 문자열을 넘겨주고 받기 위해서는 PChar형을 쓴다.
function SetStr(psStr:PChar):integer; stdcall; external 'dtest.dll';

function GetStr(psStr:PChar):integer; stdcall; external 'dtest.dll';

procedure TfrmMain.btnTestClick(Sender: TObject);
var
  nNo:Integer;
  psStr:PChar;
  sString:String;
  nWork:Integer;

begin

  // 문자열 초기화
  psStr := Nil;
  // 문자열 사이즈 잡기
  psStr := AllocMem(50);

  sString := edtStr.Text;

  // String형을 PChar형으로 캐스트
  psStr := PChar(sString);

  // dll에 문자열 입력
  SetStr(psStr);

  // 문자열 초기화
  psStr := Nil;
  // 문자열 사이즈 잡기
  psStr := AllocMem(50);

  // dll에서 문자열 가져오기
  GetStr(psStr);

  // PChar형에서 String형으로 바꾸기
  sString := StrPas(psStr);

  lblStr.Caption := sString;
 
  if psStr <> Nil then FreeMem(psStr);

end;

end.



Posted by 띠리
VB에서 호출한 DLL 함수에 넘긴 파라미터의 값이 DLL 함수안에서 바뀌어진 것이
VB에서 넘긴 변수에 적용되게 하려할때, DLL 함수의 Declare에서
인수앞에 반드시 ByRef가 아니라 ByVal이 와야한다.

# VB소스


Declare Function ByRefTest Lib "RefData.dll" (ByVal sData As String) As Integer




# C소스

extern "C" int PASCAL EXPORT ByRefTest (char* pszData)

{

    sprintf(pszData, "DLL TEST");


    return 0;

}









 

Posted by 띠리
msado15.dll을 import하여 MFC로 DLL을 만들때 빌드하면
아래와 같은 형식 재정의 에러가 뜬다.

오류 1 error C2011: 'LockTypeEnum' : 'enum' 형식 재정의 d:\data\source\adll\adll\debug\msado15.tlh 228
오류 2 error C2011: 'DataTypeEnum' : 'enum' 형식 재정의 d:\data\source\adll\adll\debug\msado15.tlh 276
오류 3 error C2011: 'FieldAttributeEnum' : 'enum' 형식 재정의 d:\data\source\adll\adll\debug\msado15.tlh 321
오류 4 error C2011: 'EditModeEnum' : 'enum' 형식 재정의 d:\data\source\adll\adll\debug\msado15.tlh 343
오류 5 error C2011: 'RecordStatusEnum' : 'enum' 형식 재정의 d:\data\source\adll\adll\debug\msado15.tlh 352
오류 6 error C2011: 'ParameterDirectionEnum' : 'enum' 형식 재정의 d:\data\source\adll\adll\debug\msado15.tlh 616
오류 7 error C2065: 'adLockReadOnly' : 선언되지 않은 식별자입니다. d:\data\source\adll\adll\adll.cpp 101

위와 같은 에러가 뜨면 주저말고 stdafx.h 파일에 밑의 코드를 찾는다.

#ifndef _AFX_NO_DAO_SUPPORT

#include <afxdao.h>            // MFC DAO 데이터베이스 클래스입니다.

#endif // _AFX_NO_DAO_SUPPORT


위의 코드를 밑의 코드처럼 커맨트 처리하고 다시 리빌드하면 재정의 에러는 없어진다.
afxdao.h에도 같은 정의가 되어있나보다.

//#ifndef _AFX_NO_DAO_SUPPORT

//#include <afxdao.h>            // MFC DAO 데이터베이스 클래스입니다.

//#endif // _AFX_NO_DAO_SUPPORT






Posted by 띠리
◆ VB에서  DLL참조


Option Explicit On


Module mdlMain


    Declare Function APlusB Lib "aigo.dll" (ByVal csLog As String) As Integer


    Public Function dllTest(ByVal nA As String, ByVal nB As String) As Integer


        dllTest = APlusB(nA , nB )


    End Function


End Module



◆ C의 간단 DLL


// aigo.cpp

#include "stdafx.h"

#include "aigo.h"


int WINAPI APlusB(int nA, int nB)
{

    return nA + nB;

}



// aigo.h

#pragma once


#ifndef __AFXWIN_H__

    #error "PCH에 대해 이 파일을 포함하기 전에 'stdafx.h'를 포함합니다."

#endif


#ifndef __AIGO__

#define __AIGO__

    int WINAPI APlusB(int nA, int nB);

#endif




; dLog.def : DLL에 대한 모듈 매개 변수를 정의합니다.


LIBRARY      "aigo"


EXPORTS

    ; 명시적 내보내기를 여기에 사용할 수 있습니다.

    APlusB



C에서 만든 DLL을 VB의 실행 파일이 있는 곳에 두고 실행을 한다.




Posted by 띠리
우선 예제만 간단한 예제 소개부터...
그냥 밑에 소스를 가지고 프로그램을 만들기만 하면 된다.
DLL만들때는 VS2005에서 만들때는
빈 프로젝트를 만들어서 소스 파일을 하나 추가해서 아래 소스를 붙여 넣는다.
메뉴의 프로젝트(P) > ??? 속성(P)...을 선택한다.
구성 속성 > 일반을 선택한뒤
프로젝트 기본값의 구성형식을 동적 라이브러리(.dll)을 선택하면 된다.

DLL 불러쓰기의 프로젝트는 그냥 Win32콘솔 응용 프로그램에서
빈 프로젝트를 만들어서 소스를 추가하면 된다.
문자 집합은 멀티바이트 문자 집합 사용을 선택하면 밑에 소스를 그대로 쓸수있다.


초간단 DLL만들기


#include <stdio.h>

#include <windows.h>


#define EXPORT extern "C" __declspec(dllexport)


// DLL을 로드한 곳에서 EXPORT한 함수명을 쓸수있게 함

EXPORT int APlusB(int nA, int nB);

EXPORT int AMinusB(int nA, int nB, int& nVal);


BOOL APIENTRY DllMain(HANDLE hModule,

                      DWORD ul_reason_for_call,

                      LPVOID lpReserved)

{

    return TRUE;

}


EXPORT int APlusB(int nA, int nB)

{

    return nA + nB;

}


EXPORT int AMinusB(int nA, int nB, int& nVal)

{

    if(nA > nB)

    {

        nVal = nA - nB;

        return TRUE;

    }

    else

        return FALSE;

}


DLL불러쓰기


#include <stdio.h>

#include <windows.h>


// DLL에서 호출할 함수의 형

typedef int (*APlusB)(int, int);

typedef int (*AMinusB)(int, int, int&);


int main()

{

    HINSTANCE    hInst;


    APlusB    fAPlusB;

    AMinusB    fAMinusB;


    // DLL 로드

    hInst = LoadLibrary("SmallDll.dll");


    if(hInst == NULL)

        return 1;


    // 호출한 함수를 맵핑

    fAPlusB = (APlusB)GetProcAddress(hInst, "APlusB");

    fAMinusB = (AMinusB)GetProcAddress(hInst, "AMinusB");


    int            nA = 700;

    int            nB = 200;

    int            nRet = 0;

    int            nRetVal = 0;


    nRet = fAPlusB(nA, nB);

    printf("%d + %d = %d\n",nA, nB, nRet);


    nRet = fAMinusB(nA, nB, nRetVal);

    if(nRet)

        printf("%d - %d = %d\n",nA, nB, nRetVal);

    else

        printf("%d < %d \n",nA, nB);


    // DLL 언로드

    FreeLibrary(hInst);


    return 0;

}



Posted by 띠리
BLOG main image
프로그래밍 공부하면서 써가는 개인 노트 (따라서 여기에 씌여있는 소스의 신빙성을 보장 못함 -.-;;) 이 블로그 보면서 틀린 점이 있으면 꼬옥 알려주세요. by 띠리

공지사항

카테고리

분류 전체보기 (323)
Win32 SDK 초보 (27)
통신관련 (11)
MFC TIP (20)
C/C++ TIP (10)
개발기타 (10)
링크 (2)
견물생심 (24)
이것저것 (8)
용어메모 (3)
데이터베이스 (32)
비주얼 베이직 (10)
하드웨어 (3)
C# (42)
Xcode (3)
델파이 (82)
홈페이지 (5)
MindStorm (0)
낙서 (5)
스크래치 (0)
기타 (6)
아두이노 (1)
라즈베리파이 (2)
안드로이드 (6)
파이썬 (0)
WEB (2)
Total : 987,317
Today : 395 Yesterday : 409