Visual Studio에서 빌드(Build) 또는 디버그(Debug) 시에 발행하는 경고 오류가 하기와 같은 형식으로 발생할 경우 해결 방법

※ 경고라서 특별히 프로그램 실행하는데 문제는 안되나… 꺼림직해서…

경고    1    포함된 interop 어셈블리 'c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\EnvDTE.dll'에 대한 참조는 ‘~~~~~~~~~~~~~.dll' 어셈블리가 해당 어셈블리에 대해 만든 간접 참조로 인해 만들어졌습니다. 두 어셈블리 중 하나에서 'Interop 형식 포함' 속성을 변경하십시오.   ~~~~

경고    2    포함된 interop 어셈블리 'c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\EnvDTE80.dll'에 대한 참조는 ‘~~~~~~~~~~~~~.dll' 어셈블리가 해당 어셈블리에 대해 만든 간접 참조로 인해 만들어졌습니다. 두 어셈블리 중 하나에서 'Interop 형식 포함' 속성을 변경하십시오.    ~~~~~

상기 오류 발생한 경우 프로젝트 참조에서 해당 참조를 선택하여 “Interop 형식 포함(Embed Interop Types)”를 False로 변경한다.

   

출처 : 자작(userpark.net)

 

해당 실행 파일에 본 첨부파일을 풀어 주면 클라이언트 없이도 배포 가능합니다.

별도로  TNS_ADMIN 경로를 환경설정에서 해줘도 되나, 10g Client 버전부터는 TNS를 이용안하고 설정 가능합니다.

실행파일과 같은 경로에 배포하시면 쉽게 사용 가능합니다.

DataGridView를 이용하였습니다.

OracleConnection oraConn = new OracleConnection();
oraConn.ConnectionString = string.Format("USER ID={0};PASSWORD={1};DATA SOURCE={2};PERSIST SECURITY INFO=false", "{오라클UserID}", "{오라클UserPassword}", "{Oracle Host Name(Domain) or Oracle Host IP}:{Oracle Service Port}/{Oracle Service Name(SID)");
//기본포트 1521를 쓸경우 ":{Oracle Service Port}" 생략 가능
oraConn.Open();
string SQL = "SELECT * FROM TAB";
OracleCommand oraCmd = new OracleCommand(SQL, oraConn);
OracleDataAdapter oraAdapter = new OracleDataAdapter(oraCmd);
DataTable dt = new DataTable();
oraAdapter.Fill(dt);
dataGridView1.DataSource = dt;

ODP.NET 버전은 11.2.0.3.0 32bit 버전을 이용하였습니다.

Oracle.DataAccess.dll 파일 버전은 2.112.3.0 입니다.

 .NET 2.0 기준 DLL파일입니다.

.NET 4.0으로 프로젝트 세팅을 하셔도 참조시 Oracle.DataAccess.dll버전을 2.x대를 추가하면 .NET 4.0에서도 사용 가능합니다.

(최대 파일당 10M라서 분할하여 올립니다.)

================================================================================================================

ODP.NET(v2).zip

 

ODP.NET(v2).z01

 

ODP.NET(v2).z02

 

ODP.NET(v2).z03

 

ODP.NET(v2).z04

 

 

추가로 Oracle.DataAccess.dll 4.112.3.0 버전도 올립니다.

 

Oracle.DataAccess(v4).zip

다음은 동적(Static) DLL로 구현 되어져 있다.

  • 정적(Static) DLL 이란? 본래의 DLL이라기 보다는 코드의 큰 함수들을 따로 모듈별로 분리 했다가 실행 시 함께 처리한다.
  • 동적(Dynamic) DLL(런타임 로딩) 이란 ? DLL은 GetProcAddress라는 API를 사용하여 필요에 따라 첨가하고 필요에 따라 해제시킬 수 있다.

DLL 소스 및 정적 DLL 호출 방법은 다음 글을 참고 하기 바란다.

2010/04/27 - [Language/Delphi] - [Delphi] DLL 호출 규칙(Calling Convention) 테스트용 DLL 및 Source

다음 소스는 Delphi 2010에서 제작 되었으나 기본 사용법은 동일하다.


  1: unit UB_Main_DynDLL;
  2: 
  3: interface
  4: 
  5: uses
  6:   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7:   Dialogs, StdCtrls;
  8: 
  9: type
 10:   TfrmUBMainDynDLL = class(TForm)
 11:     btnPascal: TButton;
 12:     edtPascal: TEdit;
 13:     btnCdecl: TButton;
 14:     edtCdecl: TEdit;
 15:     btnStdcall: TButton;
 16:     edtStdcall: TEdit;
 17:     procedure btnPascalClick(Sender: TObject);
 18:     procedure btnCdeclClick(Sender: TObject);
 19:     procedure btnStdcallClick(Sender: TObject);
 20:   private
 21:     { Private declarations }
 22:   public
 23:     { Public declarations }
 24:   end;
 25: 
 26: var
 27:   frmUBMainDynDLL: TfrmUBMainDynDLL;
 28: 
 29: implementation
 30: 
 31: const
 32:   ImportDLLFile = 'UPCallTypeDLLTest.dll';
 33:   Msg = '안녕! 세계 (Hellow World!)';   // Hellow World! 국산화 버전 ㅎㅎ
 34: 
 35: var
 36:   DLLCallTypePascal : function (AStr : string)  : PChar;  pascal;
 37:   DLLCallTypeCdecl  : function (AStr : string)  : PChar;  cdecl;
 38:   DLLCallTypeStdcall: function (AStr : string)  : PChar;  stdcall;
 39: 
 40: {$R *.dfm}
 41: 
 42: procedure TfrmUBMainDynDLL.btnCdeclClick(Sender: TObject);
 43: var
 44:   H : HINST;
 45:   S : PChar;
 46: begin
 47:   H := LoadLibrary(PChar(ImportDLLFile));
 48:   if H <= 0 then
 49:   begin
 50:     ShowMessage('로딩 에러 : ' + IntToStr(GetLastError));
 51:   end else begin
 52:     try
 53:       @DLLCallTypeCdecl := GetProcAddress(H, Pchar('DLLCallTypeCdecl'));
 54:       S := DLLCallTypeCdecl(Msg);
 55:       if H <> 0 then
 56:         edtCdecl.Text := S
 57:       else
 58:         edtCdecl.Text := 'ERROR!!';
 59:     finally
 60:       FreeLibrary(H);
 61:     end;
 62:   end;
 63: end;
 64: 
 65: procedure TfrmUBMainDynDLL.btnPascalClick(Sender: TObject);
 66: var
 67:   H : HINST;
 68:   S : PChar;
 69: begin
 70:   H := LoadLibrary(PChar(ImportDLLFile));
 71:   if H <= 0 then
 72:   begin
 73:     ShowMessage('로딩 에러 : ' + IntToStr(GetLastError));
 74:   end else begin
 75:     try
 76:       @DLLCallTypePascal := GetProcAddress(H, Pchar('DLLCallTypePascal'));
 77:       S := DLLCallTypePascal(Msg);
 78:       if H <> 0 then
 79:         edtPascal.Text := S
 80:       else
 81:         edtPascal.Text := 'ERROR!!';
 82:     finally
 83:       FreeLibrary(H);
 84:     end;
 85:   end;
 86: end;
 87: 
 88: procedure TfrmUBMainDynDLL.btnStdcallClick(Sender: TObject);
 89: var
 90:   H : HINST;
 91:   S : PChar;
 92: begin
 93:   H := LoadLibrary(PChar(ImportDLLFile));
 94:   if H <= 0 then
 95:   begin
 96:     ShowMessage('로딩 에러 : ' + IntToStr(GetLastError));
 97:   end else begin
 98:     try
 99:       @DLLCallTypeStdcall := GetProcAddress(H, Pchar('DLLCallTypeStdcall'));
100:       S := DLLCallTypeStdcall(Msg);
101:       if H <> 0 then
102:         edtStdcall.Text := S
103:       else
104:         edtStdcall.Text := 'ERROR!!';
105:     finally
106:       FreeLibrary(H);
107:     end;
108:   end;
109: end;
110: 
111: end.
112: 





출처 : 자작(http://userpark.net)

DLL호출 규칙에 대하여 먼저 언급하고자 한다.

Delphi는 다른 랭귀지에서 사용된 함수들을 쉽게 호출 할 수 있도록 여러 가지 호출 규칙을 가진다.

아래와 같이 5가지 방법이 있다.

  1. Register(__fastcall) : 레지스터에 등록하여 호출하는 방식
  2. stdcall(__stdcall) : 가장 일반적으로 활용하는 방식으로 윈도우 표준
  3. cdecl(__cdecl) : 가변인자가 있는 경우
  4. pascal(__pascal) : Delphi 최적화 코드를 생성하고자 할 때 스택으로 인자를 저장하는 방식보다는 레지스터를 경유하여 속도 향상
  5. safecall : OLE 방식에서 이용

참고 : http://www.delmadang.com/community/bbs_view.asp?bbsNo=3&bbsCat=42&indx=195902&keyword1=dll&keyword2=호출규칙

※pascal 경우 속도 향상이 된다고 책에는 나와있으나 실제 테스트는 하지 않음.

본 글에서는 stdcall,cdecl,pascal 3가지에 대해서만 샘플을 제공하여 드리겠습니다.

아래 소스를 콜타입들을 바꿔가면서 DLL과 App에서 서로 테스트 해보기시기 바랍니다.

그런데 이상하게도 테스트 하면서 stdcall과 pascal은 서로 상호 호환이 완벽하게 이루어졌습니다. 이 해당 소스만이 그런건지 정확히는 현재 알 수 없습니다.

지금까지 저는 DLL을 안 쓰다가 이번에 새로운 일이 생겼는데 DLL을 호출해서 사용해야 할 일이 생겨서 이것 저것 테스트 하다가 이런 문서도 만들게 되었습니다.

(기존 Delphi만 이용시에는 dcu파일로 배포등을 하여 DLL의 필요성을 알지 못했습니다. ^^;;)

정확한 개념이 잡힌 소스인지… 저도 장담을 못하지만 여러가지로 도움이 되었으면 좋겠습니다.

또한 DLL 생성 및 Delphi 기초에 대하여서는 기타 유용한 사이트 및 책을 이용하시기 바랍니다.


아래 방법으로 공부하시기 바랍니다.(저도 이걸로 공부 중)

  1. UPCallTypeDLLTest.dll 을 만든다
  2. UPCallTypeDLLTestApp.exe에서 상기 DLL을 호출한다.
  3. DLL과 APP를 서로 콜방식을 변경하여 가면서 오류 사항 및 반영내용을 스스로 공부한다.

아래 소스는 Delphi 2010으로 만들었습니다.(Delphi 7로 하려다가 그냥~)


Import 해 올 DLL 파일을 반드시 아래 경로에 존재 하여야 한다.
  • [%SystemRoot%] (Windows 디렉토리)
  • [%SystemRoot%]\system32\ 경로 (Microsoft Windows XP 일 경우)
  • 실행파일(현재 작업) 디렉토리에 같이 위치
  • 환경변수 PATH 상의 경로(비추천)

  • UPCallTypeDLLTest.dll
  1: library UPCallTypeDLLTest;
  2: 
  3: uses
  4:   SysUtils,
  5:   Classes,
  6:   Dialogs;
  7: 
  8: {$R *.res}
  9: 
 10: function DLLCallTypePascal(AStr : string) : PChar; pascal;
 11: var
 12:   S : string;
 13: begin
 14:   S := AStr;
 15:   S := 'DLL 함수 테스트 : ' + S + ' - pascal';
 16:   ShowMessage(S);
 17:   Result := PChar(S);
 18: end;
 19: 
 20: function DLLCallTypeCdecl(AStr : string) : PChar; cdecl;
 21: var
 22:   S : string;
 23: begin
 24:   S := AStr;
 25:   S := 'DLL 함수 테스트 : ' + S + ' - cdecl';
 26:   ShowMessage(S);
 27:   Result := PChar(S);
 28: end;
 29: 
 30: function DLLCallTypeStdcall(AStr : string) : PChar; stdcall;
 31: var
 32:   S : string;
 33: begin
 34:   S := AStr;
 35:   S := 'DLL 함수 테스트 : ' + S + ' - stdcall';
 36:   ShowMessage(S);
 37:   Result := PChar(S);
 38: end;
 39: 
 40: exports
 41:   DLLCallTypePascal
 42:   ,DLLCallTypeCdecl
 43:   ,DLLCallTypeStdcall
 44:   ;
 45: 
 46: begin
 47: end.

다음은 정적(Static) DLL로 구현 되어져 있다.

  • 정적(Static) DLL 이란? 본래의 DLL이라기 보다는 코드의 큰 함수들을 따로 모듈별로 분리 했다가 실행 시 함께 처리한다.
  • 동적(Dynamic) DLL(런타임 로딩) 이란 ? DLL은 GetProcAddress라는 API를 사용하여 필요에 따라 첨가하고 필요에 따라 해제시킬 수 있다.
  • UPCallTypeDLLTestApp.exe
      - UB_Main.pas
  1: unit UB_Main;
  2: 
  3: interface
  4: 
  5: uses
  6:   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  7:   Dialogs, StdCtrls;
  8: 
  9: type
 10:   TfrmUBMain = class(TForm)
 11:     btnPascal: TButton;
 12:     edtPascal: TEdit;
 13:     btnCdecl: TButton;
 14:     edtCdecl: TEdit;
 15:     btnStdcall: TButton;
 16:     edtStdcall: TEdit;
 17:     procedure btnPascalClick(Sender: TObject);
 18:     procedure btnCdeclClick(Sender: TObject);
 19:     procedure btnStdcallClick(Sender: TObject);
 20:   private
 21:     { Private declarations }
 22:   public
 23:     { Public declarations }
 24:   end;
 25: 
 26: var
 27:   frmUBMain: TfrmUBMain;
 28: 
 29: const
 30:   ImportDLLFile = 'UPCallTypeDLLTest.dll';
 31:   Msg = '안녕! 세계 (Hellow World!)';   // Hellow World! 국산화 버전 ㅎㅎ
 32: 
 33: function DLLCallTypePascal(AStr : string)   : PChar;  pascal;   external ImportDLLFile;
 34: function DLLCallTypeCdecl(AStr : string)    : PChar;  cdecl;    external ImportDLLFile;
 35: function DLLCallTypeStdcall(AStr : string)  : PChar;  stdcall;  external ImportDLLFile;
 36: function DLLCallTypeStdcallNickName(AStr : string)  : PChar;  stdcall;  external ImportDLLFile name 'DLLCallTypeStdcall';
 37: 
 38: implementation
 39: 
 40: {$R *.dfm}
 41: 
 42: procedure TfrmUBMain.btnCdeclClick(Sender: TObject);
 43: var
 44:   S : PChar;
 45: begin
 46:   S := DLLCallTypeCdecl(Msg);
 47:   edtCdecl.Text := S;
 48: end;
 49: 
 50: procedure TfrmUBMain.btnPascalClick(Sender: TObject);
 51: var
 52:   S : PChar;
 53: begin
 54:   S := DLLCallTypePascal(Msg);
 55:   edtPascal.Text := S;
 56: end;
 57: 
 58: procedure TfrmUBMain.btnStdcallClick(Sender: TObject);
 59: var
 60:   S : PChar;
 61: begin
 62:   S := DLLCallTypeStdcall(Msg);
 63:   edtStdcall.Text := S;
 64: end;
 65: 
 66: end.

 출처 : 자작(http://userpark.net)


PS) 본 소스를 바탕으로 .NET C#과 Visual Basic에서 호출하여 사용하는 방법을 추후 올리겠습니다.

      (대부분 C++에서 DLL을 만드는데 전 C++이 약한 관계로 ^^;;)

UPCallTypeDLLTestForDelphi2010.7z

UPCallTypeDLLTestAppForDelphi2010.7z


UPCallTypeDLLTestGroupForDelphi2010(All).7z


VS2008에서 User32.DLL 파일을 Import하여 함수를 호출하는 방법입니다.
이런 방식을 동적DLL호출(?) 이라고 하는지 모르겠습니다.
델파이에서는 동적호출이라고 명하는 걸로 기억하고 있습니다.
 
코드 설명은 간략히 기술하겠습니다.
길어봐야 별거 없다고 판단되어집니다. ^^ 
주의할 사항은 Import 해 올 DLL 파일을 반드시 아래 경로에 존재 하여야 한다.
  • [%SystemRoot%] (Windows 디렉토리)
  • [%SystemRoot%]\system32\ 경로 (Microsoft Windows XP 일 경우)
  • 실행파일(현재 작업) 디렉토리에 같이 위치
  • 환경변수 PATH 상의 경로(비추천)

  1: using System;
  2: using System.Runtime.InteropServices;
  3: using System.Collections.Generic;
  4: using System.ComponentModel;
  5: using System.Data;
  6: using System.Drawing;
  7: using System.Linq;
  8: using System.Text;
  9: using System.Windows.Forms;
 10: 
 11: namespace DLLImport
 12: {
 13:     public class UserImportDLL
 14:     {
 15:         [DllImport("User32.dll")]
 16:         public static extern int MessageBox(int hParent, string Message, string Caption, int Type);
 17:     }
 18: 
 19:     public partial class Form1 : Form
 20:     {
 21:         
 22: 
 23:         public Form1()
 24:         {
 25:             InitializeComponent();
 26:         }
 27: 
 28:         private void button1_Click(object sender, EventArgs e)
 29:         {
 30:             UserImportDLL.MessageBox(0, "모든 프로그램의 기본은 항상 '헬로우 월드!'", "Message Box Title", 0);    
 31:         }
 32:     }
 33: }

Line 2: using System.Runtime.InteropServices; // DLLImport를 정의 하고 있는 네임스페이스

Line 15, 16에서 Import 및 사용할 함수 정의

Line 30에서 호출하여 사용


※ 호출 시 Class안에 쌓아서 호출하였으나 그냥 호출도 가능 => 밑줄 친 소스만 잘 활용하면 가능함

출처 : 자작(http://userpark.net)

+ Recent posts