출처 : http://nazelm.egloos.com/938351

WshShell.Run

Dim WshShell : Set WshShell = WScript.CreateObject("WScript.Shell")
intError = WshShell.Run (strCommand [, intWindowStyle] [, bWaitOnReturn])
WshShell.Run strCommand [, intWindowStyle] [, bWaitOnReturn]
' cf, 반환값 없이 단독 실행시에는 괄호로 묶지 않는다.

- intError : Error code (실행결과로 해당 명령줄에 의해 실행된 프로그램이 반환하는 정수값)
- strCommand : 명령줄 문장
- intWindowStyle : Optional. ([]로 묶인 항목은 전부 선택항목)
0    Hides the window and activates another window.
1    Activates and displays a window.
     If the window is minimized or maximized, the system restores it to its original size and position.
     An application should specify this flag when displaying the window for the first time.
2   Activates the window and displays it as a minimized window. 
3   Activates the window and displays it as a maximized window. 
4   Displays a window in its most recent size and position.
     The active window remains active.
5   Activates the window and displays it in its current size and position.
6   Minimizes the specified window and activates the next top-level window in the Z order.
7   Displays the window as a minimized window. The active window remains active.
8   Displays the window in its current state. The active window remains active.
9   Activates and displays the window.
    If the window is minimized or maximized, the system restores it to its original size and position.
     An application should specify this flag when restoring a minimized window.
10  Sets the show-state based on the state of the program that started the application.

- bWaitOnReturn : Optional. Boolean Value(True / False, default is False)
명령줄에 의한 프로그램 실행이 완료될 때까지 기다릴 것인지 여부.
기본값은 False 이며, 생략시 해당 명령줄에 의한 프로그램 실행 여부와 상관없이
자동으로 0을 반환하고, 다음 스크립트가 계속 진행된다. (cf, 에러코드가 아닌 0을 반환)
cf, SendKey 메소드 등과 같이 쓸 때 True(기다림) 옵션을 적용해도 다음 스크립트가
    계속 진행된다. (SendKeys 에서의 순차실행 트릭 - 아래 Do While문 참조..)

WshShell.Run "%windir%\notepad" & WScript.ScriptFullName
intError = WshShell.Run("notepad " & WScript.ScriptFullName, 1, True)

Dim WshShell : Set WshShell = WScript.CreateObject ("WScript.Shell")
Dim oExec : Set oExec = WshShell.Exec("calc")

' 순차 실행 트릭 (Do While 문으로 Status 가 0 일 경우,
' = 프로그램 실행중 일 경우 Sleep으로 계속 창이 떠 있게 한다.)
Do While oExec.Status = 0
     WScript.Sleep 100
Loop

WScript.Echo oExec.Status
'  Status =  0  : The job is still running.
'  Status =  1  : The job has completed.


* 비교 : Run 과 Exec
'=> Run 은 실행만 하지만, Exec는 실행과 동시에 개체(object)를 생성한다.
'    따라서 Exec를 통한 실행은 생성된 개체를 이용한 후속 작업이 용이하다.

Set WshShell = Wscript.CreateObject("Wscript.Shell")
Set FSO = CreateObject("Scripting.FileSystemObject")
TempName = FSO.GetTempName
TempFile = TempName

WshShell.Run "cmd /c ping -n 3 -w 1000 157.59.0.1 >" & TempFile, 0, True
Set TextFile = FSO.OpenTextFile(TempFile, 1)
Do While TextFile.AtEndOfStream <> True
    strText = TextFile.ReadLine
    If Instr(strText, "Reply") > 0 Then
        Wscript.Echo "Reply received."
        Exit Do
    End If
Loop

TextFile.Close
FSO.DeleteFile(TempFile)


' 아래는 동일한 결과는 가지는 Exec 를 통한 실행이다.
' 실행을 통해 반환되는 StdOut 에 접근하기위해 임시파일 만들고 할 필요가 없다.

Dim WshShell : Set WshShell = WScript.CreateObject("WScript.Shell")
Dim ExecObject : Set ExecObject = WshShell.Exec ("cmd /c ping -n 3 -w 1000 157.59.0.1")
Do While Not ExecObject.StdOut.AtEndOfStream
    strText = ExecObject.StdOut.ReadLine
Loop

AND

출처: http://cafe.naver.com/comil.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=1144

 DLL과 VB의 사용 방법에 대하여 설명한다. 즉 C DLL을 만드는 방법에 대한 부분과 C DLL 함수를 VB에서 콜하는 방법으로 구분된다.

C DLL을 만드는 방법
1.0 DLL이란 무엇인가?
1.1 DLL을 사용하는 이유
1.2 DLL의 핵심
1.3 DLL 메모리 관리 문제
1.4 VC++를 이용 DLL을 만들기
1.5 C DLL의 예제

C DLL 함수를 VB에서 콜하는 방법
2.0 VB에서 DLL 콜하기
2.1 DLL 매개변수
2.2 문제 해결
2.3 VB에서 콜하는 프로그램의 예제
추가 정보
C DLL을 만드는 방법 1. DLL이란 무엇인가?
DLL (Dynamic Link Libraries)는 윈도우의 주요한 특징이다. DLL은 실행 프로그램에서 콜할 수 있는 함수들을 포함한다. 다시 말하면 DLL은 프로그램 에서 동적으로 링크 가능한 함수들의 라이브러리이다
링크는 동적 링크와 정적 링크로 나뉜다. 정적 링크는 변동될 수없다. 프로그램에서 사용되는 라이브러리 함수의 어드레스 정보는 실행 파일을 만드는 시점에서 결정되며 실행 중 변경되지 않는다.
동적 링크는 필요한 때에 만들어 진다. 실행 파일에 없는 함수를 콜하면, 윈도우OS는 프로그램에서 사용할 수 있도록 DLL을 로드한다. 이때 윈도우OS는 각 함수의 어드레스를 얻어서 프로그램과 동적으로 링크한다.

1.1 DLL을 사용하는 이유

DLL을 사용하는 4가지 이유가 있다: 1. C Run-Time 함수를 이용하기 위해:
C Run-Time 라이브러리는 비주얼 베이직에서는 DLL이외에는 이용할 수없는 많은 유용한 함수들을 갖는다.  
2. 콜백 루틴을 포함하는 윈도우API (Application Programming Interface)
함 수를 호출하기위해 일부 윈도우 API 함수는 콜백 함수를 필요로 한다. 콜백함수는 API 콜을 실행할 때 윈도우에서 실행할 함수이다. 이런 종류의 함수로는 EnumTaskWindows 함수가 있는데, 특정 타스크에서 소유되는 모든 윈도우 핸들을 준다.
3. 속도 개선을 위해
C는 native machine code와 유사한 수준의 완전히 컴파일된 언어이다. 즉 C로 잘 만들어진 프로그램의 실행 속도가 빠름을 의미한다.  
4. 사용시 로드하기 위해 DLL의 코드와 데이터는 필요한 경우에만 로드된다. 또한 DLL은 전체가 로드되지 않고 필요한 부분만 로드될 수도 있다. 이것은 로드되는 시간과 메모리를 줄일 수있다.  
1.2 DLL의 핵심

모든 DLL은 실행 프로그램에서 콜 할 수있도록 익스포트 함수에 추가로 반드시 LibMain 함수와 윈도우 종료 프로시저(WEP)를 포함한다.

LibMain:
DLL은 반드시 LibMain 함수를 포함한다. LibMain 함수는 DLL 초기화 위해 시스템에서 콜된다. LibMain은 DLL 로드가 필요한 첫번째 프로그램에 의해 한번만 로드된다. 다음은 LibMain의 매개변수이다:

- HANDLE : DLL 인스턴스 핸들.
- WORD : 라이브러리의 데이터 세그먼트.
- WORD : 힙 크기.
- LPSTR : 커맨드 라인 매개변수.
- WEP: WEP (Windows Exit Procedure)은 DLL이 언로드되기 전에 클린업을 수행한다. 윈도우 3.1이전의 윈도우 OS에서는 모든 DLL에서 WEP함수가 반드시 필요했으나, 3.1이후에는 선택적이다. WEP은 모듈 정의 파일(.DEF)에 반드시 정의되어야 한다:
EXPORTS WEP
- 익스포트 함수: DLL에서 콜되기를 원하는 함수들이다. _export로 표시되며, DEF 파일에 리스트된 모든 함수들을 콜할 수있다.

1.3 DLL 메모리 관리 문제

Large 메모리 모델
C 는 프로그램 힙 공간에 모든 Static과 Global(함수의 외부에 정의된) 변수에 저장하고, 그외의 변수는 스택에 저장한다. Small 과 Medium 모델에서는 모든 포인터는 디폴트로 Near이다. 이것은 동일 데이터 세그먼트(DS), 혹은 스택 세그먼트(SS)의 16비트 오프셋으로 데이터에 접근한다는 의미이다.
불행히도 컴파일러에서 DS나 SS로 부터의 오프셋을 알 수 있는 방법이 없다.
대 부분의 프로그램은 DS와 SS가 동일 세그먼트를 포인트 하므로 문제가 되지 않으나, DLL은 좀 다른 경우이다. DLL은 자신의 데이터 세그먼트를 따로 관라하고 스택만을 콜하는 프로그램과 공유한다. 이것은 DS와 SS가 동일 위치를 포인트하지 않는다는 의미이다. 가장 쉽게 DLL을 large 메모리 모델로 만들면 모든 변수는 32-비트로 참조된다.

왜 메모리를 동적으로 할당하는가?
동 적으로 메모리 할당은 윈도우에서는 일반적이다. 큰 데이터 배열을 64K로 제한된 스택에 선언하거나 디스크 공간과 윈도우 메모리를 낭비하는 데이터 세그먼트에 선언하는 것 보다는 필요한 때 메모리를 할당하고 필요하지 않을 때 종료하는 것이 더 낫다.

메모리 할당
윈 도우에서 2가지 종류의 메모리, Local 과 Global의 메모리를 동적으로 할당할 수있다. Local메모리의 한계는 64K이고 DLL의 경우 DLL을 콜하는 프로그램과 공유한다. Global 메모리는 윈도우 로드후 이용 가능한 모든 메모리 공간이다. Local 메모리는 LocalAlloc, LocalLock, LocalUnlock, LocalFree 함수로 사용되며 다음은 예이다:
char* pszBuffer; .... pszBuffer = (char *) LocalAlloc (LPTR, 20); ... LocalFree (pszBuffer);
Local 메모리 할당이 Global 메모리 할당보다 빠르다. 그러나 Local 힙은 64K로 제한되며 DLL을 콜하는 모든 프로그램과 공유해야 한다. 즉 Local 메모리는 가능한한 작게 하는 것이 좋다. Global 메모리는 GlobalAlloc, GlobalLock, GlobalUnlock, GlobalFree 함수로 사용되며 다음은 예이다:
HGLOBAL hglb; char* pszBuffer; hglb = GlobalAlloc (GHND, 2048); // GHND moveable 하며 0으로 초기화되는 메모리를 할당한다. // 2048은 할당되는 메모리의 크기 pszBuffer = GlobalLock (hglb); ... GlobalUnlock (hglb); GlobalFree (hglb);
GlobalAlloc 함수는 4K배수로 할당한다. DLL에서 다른 프로그램과 공유하는 메모리를 할당하는 경우에는 GMEM_SHARED 플래그를 사용한다. 만약 DDE를 통해 메모리를 공유하는 경우에는 GMEM_DDESHARE 플래그를 이용 할당한다. Static 변수에 데이터 저장시 주의한다.
Global이나 Static변수를 이용하여 DLL에서 데이터를 저장하는 경우 DLL의 다음 함수 콜시 값이 바뀌어져 있을 수있다. 이렇게 저장된 데이터는 DLL을 사용하는 모든 프로그램에서 사용하게 된다. 얼마나 많은 프로그램에서 DLL을 사용하건 DLL의 인스턴스는 하나이다.
이 문제를 피하려면 필요한 구조체를 DLL에 패스하고 다시 DLL에서 리턴 받는 방법을 사용한다.

파일 핸들
어플리케이션과 DLL간의 파일 핸들은 공유할 수없다. 각 어플리케이션은 자신의 파일-핸들 테이블을 갖는다. 두 어플리케이션이 하나의 DLL을 사용하는 동일 파일을 사용하려면 각각 따로 그 파일을 오픈한다.

1.4 VC++를 이용 DLL을 만들기

다음은 비주얼 C++를 이용하여 DLL을 빌드하는 방법이다: 1. 비주얼 C++ 시작한다.
2. 다음의 옵션으로 Project의 New를 선택한다:
- Project Type을 "Windows dynamic-link library (.DLL)"로 지정
- "Use Microsoft Foundation Classes" 체크 박스를 지운다.
Options메뉴의 Project를 선택하여 지정/보기를 할 수있다.
3. 아래의 예제 C와 DEF를 Project에 추가한다.
4. Project메뉴의 Build <DLL명>.DLL 을 선택한다.
1.5 C DLL의 예제

DLL은 VB에서 콜될 GetDiskInfo 함수를 포함한다. 이 함수는 이용 가능한 디스크 공간과 현재의 드라이브 명, 볼륨 명을 리턴한다.
C 코드 예제, DISKINFO.C: #include <windows.h> #include <dos.h> int CALLBACK LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR lpszCmdLine) // 다음은 윈도우 3.1에서만 필요한 UnlockData()이다 { if (wHeapSize > 0) UnlockData (0); //라이브러리의 데이터 세그먼트를 Unlocks return 1; } void __export CALLBACK GetDiskInfo (char *cDrive, char *szVolumeName, unsigned long *ulFreeSpace) { unsigned drive; struct _diskfree_t driveinfo; struct _find_t c_file; _dos_getdrive (&drive); _dos_getdiskfree( drive, &driveinfo ); if (!_dos_findfirst( "*.*", _A_VOLID, &c_file )) wsprintf( szVolumeName, "%s", c_file.name); else wsprintf ( szVolumeName, "NO LABEL"); *cDrive = drive + 'A' -1; *ulFreeSpace = (unsigned long) driveinfo.avail_clusters * (unsigned long) driveinfo.sectors_per_cluster * (unsigned long) driveinfo.bytes_per_sector; } DISKINFO.DEF 파일 예제 LIBRARY diskinfo DESCRIPTION 'GetDiskInfo 는 VB에서 콜된다. EXETYPE WINDOWS 3.1 CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE SINGLE HEAPSIZE 4096 EXPORTS GetDiskInfo @1
주: LIBRARY명과.DEF 파일명은 반드시 DLL 파일명과 동일해야 한다. 그렇지 않으면 VB는 "Error in loading DLL"의 오류를 낸다.

C DLL 함수를 VB에서 콜하는 방법  
2. VB에서 DLL 콜하기

VB 의 DLL 함수를 포함한 모든 함수는 반드시 Declare문을 사용하여 먼저 선언한다. 함수는 반드시 Form 이나Module의 Declare 섹션에서 선언할 수있다. 만약 Form 에서 DLL 프로시저나 함수를 선언하면 해당 Form에만 Private하므로 Public하려면 반드시 Module에 선언한다. 다음은 Declare문의 예이다:
Declare Sub getdiskinfo Lib "c:\somepath\diskinfo.dll" (ByVal mydrv As String, ByVal myvol As String, free As Long)
위의 Declare 문은 DISKINFO.DLL 파일의 사용자-정의 프로시저 GETDISKINFO를 선언한다.
함수를 선언한 후에는 VB 함수처럼 콜할 수있게 된다.

2.1 DLL 매개변수

DLL은 C로 만들기 때문에 VB에서 직접 지원하지 않는 매개변수를 사용할 수있다. 그 결과 프로그래머는 패스되는 적절한 데이터 타입을 찾아야만한다.

by Value 나by Reference에 의한 아규먼트 패스
기 본적으로, VB는 모든 아규먼트를 by reference (by reference로 전달 시, VB는 32-bit far 어드레스를 제공한다.) 그러나 많은 DLL 함수들은 패스되는 아규먼트가 by value로 패스된 다고 기대한다. 이렇게 하기 위해서는 ByVal 키워드를 사용하면 된다.

8-에서16-Bit 숫자 매개변수
8-에서16-Bit 숫자 매개변수(int, short, unsigned int, unsigned , short, BOOL, WORD)는 as Integer로 선언.

32-bit 숫자 매개변수
32-bit 숫자 매개변수(long, unsigned long, DWORD)는 as LONG으로 선언.
Object 핸들
모든 윈도우 핸들을 유일한 16-bit 정수이며 by value로 패스되므로 as Integer로 선언.

문자열
문자열은 LPSTR 와 LPBYTE 데이터 타입 ( 문자들과 부호없는 문자들의 포인터)이다. 이들 변수는 (ByVal paramname As String)로 선언.

숫자 값의 포인터
숫자 값의 포인터는 ByVal 키워드를 사용하지 않도록 선언.

구조체
만약 VB 사용자-정의 타입이 DLL의 구조체와 일치하면, 구조체는 by reference 선언.
주: 구조체는 by value로 패스되지 않는다.

배열의 포인터
배열의 첫 요소를 by reference로 패스한다.

함수의 포인터
VB는 콜백함수를 지원하지 않으므로 함수의 포인터를 갖는 DLL함수는 VB에서 이용될 수없다.

널 포인터
DLL에서 널 포인터가 패스되어야 하는 경우, (ByVal paramname As Any).로 선언하고 DLL 콜시 paramname 의 값을 &0로 패스한다.

2.2 문제 해결

다음은 문제를 해결하는 방법을 설명한다.
DLL 콜 된후 시스템 리소스가 적어진다.
만약 DLL이 GDI 오브젝트를 사용하는 경우, 사용후 반드시 해제해야한다.
즉 Windows SDK (software development kit) 로 GDI 오브젝트(예로, CreateBrushIndirect)를 생성하면, 반드시 사용 후, DeleteObject로 해제해야한다

"Bad DLL Calling Convention Error"
주로 Declare문에서 잘못 ByVal을 넣거나 빼는 경우 발생하거나 잘못된 매개변수가 패스될 때 나타난다.

Error in loading DLL
이 에러는DLL 파일의 프로시저를 로드하였는데, 프로시저의 Declare문에서 선언한 DLL을 로드하지 못한 경우 발생한다. DLL의 로드 실패의 이유는 윈도우 API 함수 LoadLibrary 를 이용하여 더 자세한 정보를 알 수있다.

일반 보호 오류(GPF)
GP 오류는 프로그램에서 자신에 속하지 않는 메모리 블록에 쓰기를 하는 경우 발생한다. 다음은 대표적인 2가지 이유이다:
- 배열의 경계를 넘은 경우. C에서는 배열의 한계를 체크하지 않아 쉽게 다른 메모리에 쓰기를 할 수있다.
- 해제한 메모리의 위치 포인터를 사용하는 경우, 가장 좋은 방법은 해제한 메모리는 모두 NULL을 지정하는 방법이다.
GP 오류는 DLL 함수에 잘못된 변수 타입이 전달되는 경우에도 발생한다.

2.3 VB에서 콜하는 프로그램의 예제
VB에서 DLL을 콜하는 부분은 2 부분으로 나뉜다. 첫번째는 함수 선언부이고 두번째는 이벤트 코드에서 사용하는 부분이다.
다음은 Declare 문 예이고, Declare 문은 반드시 모듈의 Declare부나 폼의 General Declarations에서 한다.
' 다음은 Declare 부분 Declare Sub getdiskinfo Lib "c:\dllartic\diskinfo.dll" ( ByVal mydrive As String, ByVal myvolume As String, free As Long)
ByVal 을 정확히 하지 않으면 GP 오류가 날수있다. 함수의 선언후 이벤트 코드에서 사용할 수있다. 다음은 Command1 Click 이벤트에서 DLL 함수를 콜하는 경우이다.
Sub Command1_Click () Dim drive As String * 1 Dim volume As String * 20 Dim free As Long Call getdiskinfo(drive, volume, free) Text1.Text = drive Text2.Text = volume Text3.Text = Str$(free) End Sub

AND

출처: http://www.codeproject.com/KB/DLL/XDllPt2.aspx

Introduction

This series of articles discusses four common situations when working with DLLs:

Part 1 Calling a DLL C++ function from a VC++ application
Calling a DLL C++ class from a VC++ application
Part 2 Calling a DLL C++ function from a VB application
Part 3 Calling a DLL C++ class from a VB application
Part 4 Loading a C++ DLL dynamically from a VC++ application

Calling a DLL C++ function from a VB application

In Part 1, I talked about creating C++ DLLs and then using those DLLs in VC++ applications. Sometimes you would like to call a function in a C++ DLL from a VB application.

Step 1

Here is the code for DLL2, which is taken from Part 1's DLL1:

Collapse
// DLL2.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
#define DLL2_EXPORTS
#include "DLL2.h"

BOOL APIENTRY DllMain( HANDLE /*hModule*/, 
                       DWORD  ul_reason_for_call, 
                       LPVOID /*lpReserved*/
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
// GetCycleCount - private function of DLL2.cpp.  The static keyword ensures
//                 that this function name is not visible outside DLL2.cpp.
static inline unsigned __int64 GetCycleCount()
{
    unsigned int timehi, timelo;

    // Use the assembly instruction rdtsc, which gets the current
    // cycle count (since the process started) and puts it in edx:eax.
    __asm
    {
        rdtsc
        mov timehi, edx;
        mov timelo, eax;
    }

    return ((unsigned __int64)timehi << 32) + (unsigned __int64)timelo;
}


///////////////////////////////////////////////////////////////////////////////
// Example of an exported function
///////////////////////////////////////////////////////////////////////////////
// GetCpuSpeed - returns CPU speed in MHz;  for example, ~2193 will be 
//               returned for a 2.2 GHz CPU.
DLL2_API int __stdcall GetCpuSpeed()
{
    const unsigned __int64 ui64StartCycle = GetCycleCount();
    Sleep(1000);
    return static_cast((GetCycleCount() - ui64StartCycle) / 1000000);
}

DLL2.h looks like:

#ifndef DLL2_H
#define DLL2_H

// The following ifdef block is the standard way of creating macros which 
// make exporting from a DLL simpler.  The DLL2.cpp file is compiled with 
// the symbol DLL2_EXPORTS defined at the top of DLL2.cpp.  This symbol 
// should *not* be defined in any project that uses DLL2.  This way any 
// other project whose source files include DLL2.h will see DLL2_API defined 
// as __declspec(dllimport), whereas within DLL2.cpp, DLL2_API is defined as
// __declspec(dllexport).

#ifdef DLL2_EXPORTS
    #define DLL2_API __declspec(dllexport)
#else
    #define DLL2_API __declspec(dllimport)
#endif

///////////////////////////////////////////////////////////////////////////////
// This function is exported from the DLL2.dll
DLL2_API int __stdcall GetCpuSpeed();

#endif //DLL2_H

One difference between the code for DLL2 and the code for DLL1 is that GetCpuSpeed() is declared using __stdcall. This is required for any C/C++ function that you want to use with VB. Here is what MSDN says about the __stdcall calling convention:

Element Implementation
Argument-passing order Right to left.
Argument-passing convention By value, unless a pointer or reference type is passed.
Stack-maintenance responsibility Called function pops its own arguments from the stack.
Name-decoration convention An underscore (_) is prefixed to the name. The name is followed by the at sign (@) followed by the number of bytes (in decimal) in the argument list. Therefore, the function declared as int func( int a, double b ) is decorated as follows: _func@12
Case-translation convention None

Step 2

To test DLL2.dll, I use this VB code:

Collapse
Private Declare Function GetCpuSpeed Lib "DLL2.dll" () As Integer
Private Declare Sub InitCommonControls Lib "comctl32.dll" ()

Private Sub Form_Initialize()
    InitCommonControls
    ChDir App.Path

End Sub

Private Sub Command1_Click()

    Dim nSpeed As Integer
    Dim s As String
    
    Screen.MousePointer = vbHourglass
    nSpeed = GetCpuSpeed()
    Screen.MousePointer = 0
    
    s = nSpeed
    
    Form1.Text1.Text = "GetCpuSpeed() returned " + s

End Sub

Private Sub Form_Load()

    Form1.Text1.Text = ""
    
End Sub

After copying DLL2.dll to the VB directory, I run VB2.exe and this is what I see:

When I click the button, I get this:

So I know that VB can't find the function GetCpuSpeed() in the DLL. To look at what VB is seeing, I use dumpbin /exports dll2.dll from the command line:

Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

Dump of file dll2.dll

File Type: DLL

  Section contains the following exports for DLL2.dll

           0 characteristics
    403FE342 time date stamp Fri Feb 27 08:21:30 2004
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 00001010 ?GetCpuSpeed@@YGHXZ

  Summary

        4000 .data
        1000 .rdata
        1000 .reloc
        4000 .text

This is what you would expect from VC++ - it has "decorated" the name GetCpuSpeed and exports the new mangled name ?GetCpuSpeed@@YGHXZ, which VB doesn't understand. What does this mean? Here is what MSDN says:

The Microsoft C++ compilers encode the names of symbols in C++ programs to include type information in the name. This is called "name decoration", or "name mangling". The purpose of this is to ensure type-safe linking. The C++ language allows function overloading where functions with the same name are only distinguished from one another by the data types of the arguments to the functions. Name decoration enables the linker to distinguish between different versions of overloaded functions because the names of the functions are encoded or decorated differently.

Step 3

To get back to the problem that VB is having, I need to tell VB that the name associated with export #1 is GetCpuSpeed, not the VC++ decorated name. There is a simple way to do this: create a DLL2.def file, which looks like this:

; DLL2.def - defines the exports for DLL2.dll

LIBRARY DLL2
DESCRIPTION 'A C++ dll that can be called from VB'

EXPORTS
    GetCpuSpeed

Now I recompile DLL2. To check what change has been made, I run dumpbin again:

Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

Dump of file dll2.dll

File Type: DLL

  Section contains the following exports for DLL2.dll

           0 characteristics
    403F2B8D time date stamp Fri Feb 27 08:35:41 2004
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names

    ordinal hint RVA      name

          1    0 00001010 GetCpuSpeed

  Summary

        4000 .data
        1000 .rdata
        1000 .reloc
        4000 .text

This shows that export #1 is now named GetCpuSpeed. So I copy the new Dll2.dll file to the VB directory, run the VB app, and click the button. This is what I get:

Success! I have a VB application calling a function that has been exported from a VC++ DLL. But what about VC++ applications? Can a VC++ application call the same DLL as a VB application?

Step 4

I copy the EXE code from Part 1, remove the code that tests the C++ class, compile it, and run it. Here is what I get when I press the button:

So I have one DLL that can be called by a VC++ application and a VB application. This was accomplished by use of a module definition (.DEF) file.

Step 5

Since the DLL now has a .DEF file, there is no need for the __declspec(dllexport) and __declspec(dllimport). I change DLL2.h to:

#ifndef DLL2_H
#define DLL2_H

///////////////////////////////////////////////////////////////////////////////
// This function is exported from the DLL2.dll
int __stdcall GetCpuSpeed();

#endif //DLL2_H

and also update DLL2.cpp.

Key Concepts

  • To avoid name-mangling problems and having to Alias function names in VB, use module definition (.DEF) files.
  • When using .DEF files, it is not necessary to use __declspec(dllexport) or __declspec(dllimport).
  • Use __stdcall for functions that are called by VB.
  • DLLs implemented with .DEF files can be called by both VC++ and VB programs (without needing to use Alias).

Demos

The EXE2.exe and VB2.exe demos test the GetCpuSpeed() function in DLL2.dll:

Revision History

Version 1.0 - 2004 February 29

  • Initial public release

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Hans Dietrich


Mvp
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

Occupation: Web Developer
Location: United States United States

AND

출처: http://sandsprite.com/CodeStuff/Writing_A_C_Dll_for_VB.html

Author: David Zimmer
Site: http://sandsprite.com

Environment: VB6 + VC6

Writing C DLLs for use with VB



As your VB coding develops, one of the logical steps is to learn how to integrate in C/C++ code with your VB projects.

There are allot of reasons you might want to do this. C/C++ has tons of advanced code examples out there, and intristically has more built in power than VB.

This article is an introduction to writing C Dlls for use with VB. This file is not a definitive resource, I am writing this from what I know at the moment and what I have found through research, experimentation and debugging. There is actually allot of stuff to know...way more than I can describe all inline here. So take this, and the code, as a rough guide to help you on your journey. I do not have the time to detail everything or write up a complete paper on everything you need to know.

This article is half discussion, half tips and tricks. Its aim is to get experienced VB developers with some background in C up and running making standard C dlls for use with VB. I have tried to put together as practical an example as I could and kept it as clear and direct as possible. This information has been put together through the knowledge gained across many books and many test projects.

If you are new to C and maybe even writing API declarations...it will take time to soak it all up, and you will have to search out other resources to help drive in the ideas. Relevant books that I recommend include

  • Johnathan Morrison's C++ for VB Programmers - excellent resource for VB developers looking to code in C
  • Bruce Mckinney's HardCoreVB - on your VB6 MSDN CD in the Books section - old but good
  • Dan AppleMan's Guide to the WinAPi - good appendix on how VB calls DLLs
  • Matt Curland's PowerVB - Advanced VB topics mostly COM related but also some standard DLL stuff
  • VB4DLL.txt - Comes with VB, describes how VB calls C Dlls
There are allot of really crappy books out there just waiting to soak your pockets dry (Like the entire SAMS Teach yourself series)

I personally recommend all of the books above, and I consider them professional level references that are worthy of any professional developers library. (IE they are worthy references even after you have read them and worth hanging onto as opposed to the textbook type books that you skim once and never open again (like most college textbooks):

The example project download will show you several ways to pass strings to your C DLL, how to use Variants, SafeArrays, standard arrays use call back functions, and even inline asm.

First let me introduce you to some Visual C terminology. There are several ways to create a DLL using Visual C++. In VB, all of the DLLs we create are ActiveX DLLs, which means they are actually COM servers housed in a DLL file.

You can make ActiveX dlls in VC, however we are going to take a more basic approach and write a traditional standard call Dll so we don뭪 have to worry about the overhead and complexity of ATL and complex raw COM types.

In VB we are used to interacting with these traditional Dlls in the form of the Windows APIs. Traditional Dlls require the use of manually entered Declare statements to use them in VB.

To start off, C defines several possible function calling conventions. The calling convention describes in what order arguments are pushed onto the stack and who is responsible for cleaning up the stack when the function returns.

For our C-VB interactions we must use the standard calling convention (__stdcall) because this is what VB expects. If you forget to include this, you will get an error "Bad DLL Calling Convention" in the IDE when you try to run your code.

The next thing we need to know is how the map our commonly used VB type into something we can deal with in C. Because of C's power and flexibility, there are actually multiple ways we can deal with our VB Datatypes. The most basic types are those that have a direct C counter part. The VB Byte type for example, takes up one byte of memory and maps to the C char type. The VB Long takes up 4 bytes of memory and maps to the C int type.

When you look at a API declaration you may have noticed that some of the arguments are passed ByRef and some are passed ByVal. (If the API Declaration does not define either, it is ByRef by default.) So what does this mean to our C function?

Just like with VB functions, a ByRef argument means to pass in the actual variable used in the calling code. This is done by passing in a pointer to the original variable.(A pointer is just the numeric address in memory of the original variable). A ByVal argument type means to push a copy of the variables value onto the stack for the called function to use.

Lets see an example.

vb function Declare : ...(ByRef val1 as long , ByVal val2 as long)
C function Prototype: ...(int *val1, int val2)

It is very important you understand the difference between the two, when you get to be writing DLL Declare statements for your own Dlls, the ByVal, ByRef mixups are going to cost you many crashs until you get the feel for it. When you are trying out a new Declare statement or testing a new DLL it is best to compile your VB Exe first and then run that.

If you got something wrong and are running in the IDE, the entire IDE will crash and you could loose your work. Always set VB to auto save changes every time you run from the IDE to help prevent lost work. Common error messages include "Bad DLL Calling Convention", "Can not locate entry point for.." , and memory read/write exceptions caused by trying to reference memory at some low address. If your code works fine, but then crashs when the function is ready to return back to VB, you could have either used the wrong calling convention (stack not cleaned up) or you could have accidentally declared a return type from the C function but forgot it in the VB Declare (or the reverse of that)

We will get into some general debugging hints latter on.

Next we are going to look at the VB String type. for which we (again) need a little more background.

VB is based around COM and uses all COM types for its variables. The VB string we all know and love is actually a BSTR type to C. A BSTR is a Unicode string with a length prefix. This Length prefix allows the string to contain embedded nulls and makes it very easy to read the length of a string (vb Len() function uses this) .

Standard C coding usually uses char arrays (byte arrays) to hold its strings. These strings are typically held in a fixed size array and terminated with a null character ( Chr(0) ). When we work with strings between C and VB we have 2 options. We can either send the string as our native BSTRs or we can specify to have the VB runtime convert our BSTR to an standard null terminated C string for us as it makes the call. If we pass our String ByRef to the C function, it will be passed as a BSTR. If we pass it ByVal then the VB runtime will make a copy of our string, convert it to a null terminated byte array of characters, then pass that to the called function. If your C function receives the BSTR, you can use the Sys*String functions (examples provided) to read & modify it as you wish. If you are dealing with the byte array C style string, you can read and access it as you would a normal C String. If the byte array is big enough, you can actually modify its contents to be returned to VB. This is because on return the VB runtime copies the byte array back into the BSTR variable we passed into it. You cannot however exceed the bounds of the byte array you were passed or else you will be corrupting other things in memory and will most likly crash. When working with a native BSTR you are free to modify the size of the string through the Sys*String functions (ex. SysReAllocString) because the API will handles its own memory management.

The books mentioned above are good resources for more in depth discussion on this.

The next common area people wonder about is how to pass and access arrays.

There are two common ways to do this. One uses pointer arithmetic and the other uses the standard VB Array Type, which is a SAFEARRAY as C knows it. SAFEARRAY struc (Type as vb developers know it) and is defined in oleauto.h

First we will describe the pointer arithmetic method.

Array Elements are laid out sequentially in memory and take up a known number of bytes based on their type. An array of Longs will be a block of memory where every 4 bytes defines one element.

If we pass the first element of our VB Array byRef, we are actually passing in the address of the beginning of that array. If we also include how many elements are in the array as a function argument, we can now loop through all the sequential memory locations to get the corresponding values.

See the code for an example of this. A Word of caution, pointer arithmetic in C goes by the size of the type of pointer you are using. an Int type will increment 4 bytes of memory for each element accessed, a char one byte, make sure you use the right type of pointer to correspond to the VB type you pass in. Do not work with BSTR strings in this way.

The next way to work with arrays between VB and C is to use the native VB array object, the SAFEARRAY mentioned above. You can directly pass in a SAFEARRAY ** as a function argument for use in C by just including oleauto.h header file which contains its struc definition. See the example code for usage. With this method you do not need to pass in the number of array elements because it is contained in the SAFEARRAY structure.

The next thing you may want to work with is Variant data. The vb Variant type is a handy thing, it can contain data of any type and you can change it on the fly...but how is that data really held behind the scenes and how can we access it in C? The VARIANT data structure is actually a large union with a field defined for each different possible data type. An int descriptor field precedes the union and is used as a flag to mark what type of data the VARIANT union currently contains. To use a VARIANT you receive as an argument, first test its .vt member for variable type, then you can access the specific field in the structure for that type to get the actual variable data.

The last commonly sought after example I have included is that of how to perform a callback to a VB function from your C Dll by using a function pointer you supplied in VB. In C you can define function pointer variables that will let you call a function just by having its address. Vb has no such native support although there are workarounds (kind of) . Anyway when you are setting up to have C use a callback for your VB function there are a couple things worth pointing out.

First make sure to declare your function pointer prototype as __stdcall. Second, In VB your callback function has to be in a module. To get the address of a function in VB, you need to use the AddressOf operator, which only works for functions contained in modules. Why? Because forms and classes are instances that could go away. Module functions are guaranteed to exist and not change for the life of the application, so the VB designers made an attempt to save you from yourself by not letting you use it in other situations. If your adventurous, you may be able to snag a classes VTable pointer to a specific function and pass it into your C DLL for callback on class functions. I haven뭪 tried this but it sounds like fun :) I will leave that as an experiment up to the reader.. If you know what I am talking about then you can figure out how to test it ;)

One other aspect of compiling a C DLL for use with VB I should mention is the use of a .def file. The .def file is a simple format plain text file that lists the exact function names you want used in the export table of your DLL. If you do not include it, the compiler will mangle your function names to include other bits of info. Use the .def file to prevent this.

Remember, every time you add a new function to your C DLL, you are going to have to add its function name to the list of DLL Exports in the .def file, as well as write a new Declare statement to include in your VB app. If you get an error "Entry point cannot be found" when trying to run your VB app, then there is a good chance you forgot to add in the new function name to the def file exports list.

Lastly its worth noting that there are multiple ways to pass stuff. Here are some other alternatives for char pointers:
void test(char* x)
declare sub test1 lib "x.dll" alias "test" (x as byte)
declare sub test2 lib "x.dll" alias "test" (byval x as string)
declare sub test3 lib "x.dll" alias "test" (byval x as long)

dim b(50) as byte
dim s as string

s = space(50)
test1 b(0) 
test2 s
test3 varptr(b(0))


Now onto our

Debugging tips and tricks:



  1. Compile your VB exe with debugging info, Project -> Properties -> Compile tab
  2. Compile your C DLL as a debug release -> Build ->Set Active Configuration
  3. You can call the debugger from VB Code by using DebugBreak API (then press F10 twice to step to code)
If you Compiled your VB ExE with debug info, you will be able to see your VB Source interspersed with the actual Assembly that is running, with the VC other debug tools this is very powerful way to see what is going on!

If you Compiled your DLL in debug release, then you can set breakpoints in your C Code and have it drop you into the debugger when it gets called.

To get to a break point in your DLL compile a copy of your vb project in the same folder as the debug dll then press F5 in the VC IDE. It will ask you which executable you would like to startup for debugging, select the copy you placed next to the debug dll and it will load that instance of the dll which the debugger can break on. When you are at a VC breakpoint like this, you will have very nice debug tools at your fingertips that are way beyond what the VB debugger has.

Learn to use the VC debugger well..if you really want to expand your skills to that next level, it will soon become your new best friend.

If you get errors on startup that you cannot set breakpoints and some have been removed, it is probably because you are compiling in release mode without debug info. You really do not want to try to debug looking only at asm.

Downloads

Other Integration Examples :
AND

출처: http://iam-hs.com/57

사실 Visual Basic(이하 VB) 에 대해서는 잘 모릅니다...;;; (물론, 지금도 마찬가지구요...)

이번에 일을 하면서, 조금 공부하게 된 부분이 있어서 올려봅니다.

회사에서 제가 개발을 하고 있는 부분은 어떻게 보면 중간모듈입니다...

제가 만든 제품 자체가 엔드유저에게 바로 제공되는 것이 아니라....

엔드유저가 사용하게 되는 제품을 개발할 때... 제가 만든 라이브러리/모듈을 쓰거든요;;

저는 C/C++ 로 라이브러리/모듈을 개발하고... 이 라이브러리를 쓰는 업체에서도

대부분은 C/C++ 로 작업을 합니다.
.
.

근데 한번씩... 예외적으로 C/C++ 이 아닌 다른 언어를 쓰는 경우가 있습니다.

델파이랑 VB가 그 대표적인 경우인데요;;;..

이번에 VB를 사용하는 업체에서 라이브러리를 쓰겠다고 요청이 왔습니다;;;..

만약, C/C++ 업체라면 라이브러리 파일(*.LIB)과 헤더파일(*.H), 메뉴얼을 보내주면서..

"작업하다가~~ 문의사항 있음 연락하세요~~" 이러면 되는데...;;;

다른 언어의 경우는 이게 참 힙듭니다..;;

이럴때 DLL 이란게 참~~ 도움이 되는데요...

라이브러리와 동일한 기능을 수행하는 DLL 을 만들어두고... 다른 언어의 경우에는

그 DLL 을 제공하면 되는거죠~~ㅋ

-0-;; 샘플코드를 제공해달라고 하는데..-0- VB는 잘 쓰질 못하고... 난감한 상황에서;;

부랴부랴 이것저것 뒤져가며 결국엔 익혔답니다..;;


#define TEST_DLL extern "C" __declspec(dllexport)

// 1. 'int' Return
TEST_DLL int Test_Sum(int Num1, int Num2);

// 2. 'void' Return
TEST_DLL void Test_Output();

// 3. 'void*' Return
TEST_DLL void* CreateUser();

DLL 에는 위와 같은 함수들이 들어있고, 이 함수들을 VB에서 사용하려면...

먼저 아래와 같이 선언을 해줘야 합니다.

' 1. 'int' Return
Declare Function Test_Sum Lib "TestDll.dll" (ByVal Num1 As Integer, ByVal Num2 As Integer) As Integer

' 2. 'void' Return
Declare Sub Test_Output Lib "TestDll.dll" ()

' 3. 'void*' Return
Declare Function CreateUser Lib "TestDll.dll" () As Int32

기본틀은....

Declare [Function / Sub] <FunctionName> Lib "<DLL Name>" (인자들) (As 반환값)

요렇습니다...


그리고 함수 선언문을 작성할 때 알아둬야 되는 몇가지 사항들입니다.

1. 해당함수가 리턴하는 값이 없는 경우(void 형 함수)에는 Sub 를 사용한다.
2. 함수의 인자가 값을 넘겨주는 경우는 ByVal 을 주소값(포인터)를 넘겨주는 경우는 ByRef를 사용한다.
3. 선언의 제일 마지막에는 해당함수의 리턴형태를 As 를 사용해서 지정해준다.
    void* 함수처럼 포인터를 리턴하는 경우는 해당 바이트의 수에 맞는 데이터형을 지정한다.
    ( DLL 에서 32비트 데이터로 리턴을 하면 VB에서도 32비트 데이터형으로 받는다. )

선언이 끝나면 코드에서 사용이 가능한데 사용방법은 C/C++ 에서 함수의 사용과 비슷합니다.

단, Sub 로 선언한 함수의 경우는 Call 명령으로 호출해주도록 합니다.
(ex. Call Test_Output() )
.
.

이런식으로 C++ 로 만들어진 DLL을 VB에서도 사용이 가능합니다.

더 세밀한 내용은 저도 잘 모르는 관계로.. 요정도 선에서 수박겉핥기 정도로..ㅋㅋ
AND