barus's diary

とても真面目なblogですにゃ

VC++のアプリでコマンドライン(VC++)作成のDLL呼び出し (第8回) for VS2015 Express VC++

VC++でグラフィック描画サンプルテスト (第7回) for VS2015 Express VC++ - barus's diary

で作成したプロジェクトに、

f:id:hatakeka:20160918111854p:plain

DLL呼び出しテスト実行時に以下のように表示させてみる。

f:id:hatakeka:20160918110144p:plain

構成としてはこんな感じ。

 

  [VC++本体]    [コマンドライン(VC++)で作成DLL]

    呼び出し元      呼び出されるがわ

 

 

今回は両方ともVC++で作成している。

ファイルとフォルダの構成は以下のように配置した。

f:id:hatakeka:20160918104718p:plain

 

猫でもわかるC++プログラミング 第2版 猫でもわかるシリーズ

コマンドライン(VC++)でDLLファイル作成

C#作成アプリからVC++作成のDLLを使用 for VS2015 Express for Desktop - barus's diary

で作成したVC++のDLLを、MyGrahicsフォルダに配置する。

f:id:hatakeka:20160918110641p:plain

myGraphics.cpp

#include <string.h>
#include <iostream>

#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif

using namespace std;

//整数値を返す
DLLEXPORT int add(int a, int b) {
return a + b;
}

//文字列の参照値渡し
DLLEXPORT void test_str(char *str)
{
sprintf(str, "%sだにゃん♪", str);
}

これは、ファイル名だけ変えて、前回のそのまま流用。

ビルドと、移動を簡略化するために、make.batファイルを作成した。

make.bat

cl myGraphics.cpp /LD /EHsc
copy /y myGraphics.dll ..\Debug

 

コンパイルするとこんな感じ。

f:id:hatakeka:20160918111310p:plain

 for x86、36ビット用でコンパイルしている。

 

コマンドラインで、開発環境を整えるには、

Windowsアプリを作成してみよう for VS2015 Express for Desktop C#
を参考に、VS2015をインストールして下さい。

 

次に、VC++呼び出し側の紹介

 

VC++呼び出し側

f:id:hatakeka:20160918111854p:plain

メニュウの「DLL呼び出しテスト」を追加する。

3DGraphic.rc

Resource.h

を以下のように変更して下さい。

 

3DGraphic.rc

/////////////////////////////////////////////////////////////////////////////
//
// メニュー
//

IDC_MY3DGRAPHIC MENU
BEGIN
POPUP "ファイル(&F)"
BEGIN
MENUITEM "DLL呼び出しテスト(&X)", IDM_TEST
MENUITEM "アプリケーションの終了(&X)", IDM_EXIT
END
POPUP "ヘルプ(&H)"
BEGIN
MENUITEM "バージョン情報(&A)...", IDM_ABOUT
END
END

 

Resource.h


#define IDR_MAINFRAME 128
#define IDD_MY3DGRAPHIC_DIALOG 102
#define IDD_ABOUTBOX 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDI_MY3DGRAPHIC 107
#define IDI_SMALL 108
#define IDC_MY3DGRAPHIC 109
#define IDM_TEST 200
#define IDC_MYICON 2
#ifndef IDC_STATIC
#define IDC_STATIC -1

以上変更したら3DGraphic.cppの LRESULT CALLBACK WndProc()関数に

IDM_TESTのイベントを拾うSwitch()文を追加。以下(3DGraphic.cpp)参照。

 

3DGraphic.h

#pragma once

#include "resource.h"

#include <iostream>
using namespace std;

 

typedef int(*FUNC)(int x, int y);
typedef void(*FUNC2)(char *str);

 

static int paint_sw; //描画の切替フラグ
void dlltest(HWND hWnd, HDC hdc);
LPCWSTR StringToUnicode(string str); //string

 

赤い部分を追加する。

typedef部分がDLL呼び出し。

paint_swは、どのメニュウが押されたかのフラグ。

dlltest()は、3DGraphic.cppに追加した関数。

StringToUnicode()は、3DGraphic.cppに追加した関数。

 

 

 

 

 

 

3DGraphic.cpp

// 3DGraphic.cpp : アプリケーションのエントリ ポイントを定義します。
// << >> < > <>

#include "stdafx.h"
#include "3DGraphic.h"
#define MAX_LOADSTRING 100

// グローバル変数:
HINSTANCE hInst;                                // 現在のインターフェイス
WCHAR szTitle[MAX_LOADSTRING];                  // タイトル バーのテキスト
WCHAR szWindowClass[MAX_LOADSTRING];            // メイン ウィンドウ クラス名

// このコード モジュールに含まれる関数の宣言を転送します:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: ここにコードを挿入してください。

    // グローバル文字列を初期化しています。
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MY3DGRAPHIC, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // アプリケーションの初期化を実行します:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY3DGRAPHIC));

    MSG msg;

    // メイン メッセージ ループ:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}



//
//  関数: MyRegisterClass()
//
//  目的: ウィンドウ クラスを登録します。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY3DGRAPHIC));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MY3DGRAPHIC);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}

//
//   関数: InitInstance(HINSTANCE, int)
//
//   目的: インスタンス ハンドルを保存して、メイン ウィンドウを作成します。
//
//   コメント:
//
//        この関数で、グローバル変数インスタンス ハンドルを保存し、
//        メイン プログラム ウィンドウを作成および表示します。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // グローバル変数インスタンス処理を格納します。

   HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);//Window を表示
   UpdateWindow(hWnd); //表示を初期化

   return TRUE;
}

//
//  関数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    メイン ウィンドウのメッセージを処理します。
//
//  WM_COMMAND  - アプリケーション メニューの処理
//  WM_PAINT    - メイン ウィンドウの描画
//  WM_DESTROY  - 中止メッセージを表示して戻る
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
			
	    //テスト
	    case IDM_TEST:
	      paint_sw = 1;
//画面全体に再描画を要求 false(上書き) InvalidateRect(hWnd, NULL, true);
break; case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: HDC を使用する描画コードをここに追加してください..
// ---- ここから 追加 switch (paint_sw) { case 1: dlltest(hWnd, hdc); break; default: RECT rc; POINT aptPentagon[6] = { 50,2, 98,35, 79,90, 21,90, 2,35, 50,2 }, aptHexagon[7] = { 50,2, 93,25, 93,75, 50,98, 7,75, 7,25, 50,2 }; POINT *ppt = aptPentagon; int cpt = 6; GetClientRect(hWnd, &rc); SetMapMode(hdc, MM_ANISOTROPIC); SetWindowExtEx(hdc, 100, 100, NULL); SetViewportExtEx(hdc, rc.right, rc.bottom, NULL); Polyline(hdc, ppt, cpt); break; } // ---- ここまで EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // バージョン情報ボックスのメッセージ ハンドラーです。 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; } void dlltest(HWND hWnd, HDC hdc) { //DLLのパスを指定します。 LPCWSTR lpBuffer = L"myGraphics.dll"; HMODULE hModule = LoadLibrary(lpBuffer); if (NULL == hModule) { cout << "err LoadLibrary()" << endl; return; } //関数アドレスの取得 FUNC func = (FUNC)GetProcAddress(hModule, "add"); if (NULL == func) { cout << "err GetProcAddress()" << endl; return; } int ret; TCHAR wstr[1024]; ret = func(1, 2); wsprintf(wstr, L"add() = %d", ret); TextOutW(hdc, 100, 170, wstr, lstrlenW(wstr)); //関数アドレスの取得 FUNC2 func2 = (FUNC2)GetProcAddress(hModule, "test_str"); if (NULL == func) { cout << "err GetProcAddress()" << endl; return; } char strdll[1024] = "\0"; func2(strdll); string strd = strdll; string str = "DLL側の文字列は" + strd + "です。"; LPCWSTR wstr2 = StringToUnicode(str); TextOutW(hdc, 100, 200, wstr2, lstrlenW(wstr2)); } //マルチバイト文字列(Shift_JISEUC-JP)をワイド文字列(Unicode)に変換する //string型からLPCWSTR型へ LPCWSTR StringToUnicode(string str) { wchar_t *wcs = new wchar_t[str.length() + 1]; size_t ret; setlocale(LC_CTYPE, "jpn"); //mbstowcs_s(&ret, wcs, str.length() + 1, str.c_str(), _TRUNCATE); mbstowcs_s(&ret, wcs, str.length() + 1, str.c_str(), str.length()); return wcs; }

 

Unicodeって何? ・・・って方は、ググってみてください。

 

マルチバイト文字列から、ワイド文字列のところがなんだか難しいですね(;´Д`)。

ネイティブ圏以外の人はここで、躓いて時間とられるんじゃないでしょうか・・

意外とここが肝だったりする。

 

F5(ビルド) 実行時

f:id:hatakeka:20160918115922p:plain

 

f:id:hatakeka:20160918111854p:plain

 

ファイル>DLL呼び出しテストで以下のように表示されたら成功です。

f:id:hatakeka:20160918110144p:plain

 

 

終わり。

 

 

5日でC++がわかる本(日経BP Next ICT選書)