none
C#(DLL)からC++(exe)の関数の呼び出しを行いたい RRS feed

  • 質問

  • お世話になります。

    C#からC++(EXE)の関数呼び出しについて、教えてください。

    Visual Studio 2010

    Win32(C++)からWPFのコンテンツをホストして画面を作成しています。

    C++側はコンパイルに/clrオプションを設定してあります。

    C++側はExeで作成し、C#側はDLLで作成してあります。

    C++側=====================

    ref class Globals
    {
    public:
       static System::Windows::Interop::HwndSource^ gHwndSource;
       static WPFControls::TEST01^ gwcTest;
    };

    HWND GetHwnd(HWND parent, int x, int y, int width, int height)
    {
       System::Windows::Interop::HwndSourceParameters^ sourceParams =
          gcnew System::Windows::Interop::HwndSourceParameters
          ("MFCWPFApp");
       sourceParams->PositionX    = x;
       sourceParams->PositionY    = y;
       sourceParams->Height       = height;
       sourceParams->Width        = width;
       sourceParams->ParentWindow = IntPtr(parent);
       sourceParams->WindowStyle  = WS_VISIBLE | WS_CHILD;
       Globals::gHwndSource =
          gcnew System::Windows::Interop::HwndSource(*sourceParams);

       Globals::gwcTest = gcnew WPFControls::TEST01();

       FrameworkElement^ myPage = Globals::gwcTest;

       Globals::gHwndSource->RootVisual = myPage;
       return (HWND) Globals::gHwndSource->Handle.ToPointer();
    }

    C#側=====================

    namespace WPFControls
    {
        public partial class TEST01 : UserControl
        {
    InitializeComponent();
        }
        public void AAA()
        {
        }
    }

    C++側からC#側の関数の呼び出しは行えます。(Globals::gwcTest->AAA())

    C#側からC++の関数の呼び出しを行う方法をご教示いただけますか。

    よろしくお願いいたします。

    2018年12月21日 1:37

回答

  • C#から呼ばせたい関数をデリゲートで渡して呼ばせれば普通にできますが…

    #MFCやWPFを用意するのが面倒なのでC++/CLIコンソールアプリと単純なC#DLLで

    #include "stdafx.h"
    #include <locale>
    #include <iostream>
    #include <vcclr.h>
    #include <tchar.h>
    using namespace System;
    
    void CallBacked(System::Object^ param)
    {
    	if (param != nullptr)
    	{
    		System::String^ str = param->ToString();
    		pin_ptr<const wchar_t> ps= PtrToStringChars(str);
    		const wchar_t* pc = ps;
    		std::wcout << pc << std::endl;
    	}
    }
    
    void EventCallback(System::Object^ sender, System::EventArgs^ e)
    {
    	if (e != nullptr)
    	{
    		System::String^ str = e->ToString();
    		pin_ptr<const wchar_t> ps = PtrToStringChars(str);
    		const wchar_t* pc = ps;
    		std::wcout << pc << std::endl;
    	}
    }
    
    int main(array<System::String ^> ^args)
    {
    	std::locale::global(std::locale(""));
    
    	System::Action<System::Object^>^ callBack = gcnew System::Action<System::Object^>(&CallBacked);
    
    	System::EventHandler<System::EventArgs^>^ handler = gcnew System::EventHandler<System::EventArgs^>(&EventCallback);
    
    	ClassLibrary1::Class1^ c = gcnew ClassLibrary1::Class1();
    	c->AAA(callBack);
    		
    	c->CustomEvent += handler;
    	c->AAA();
    
        return 0;
    }
    C#
    using System;
    
    namespace ClassLibrary1
    {
        public class Class1
        {
            public void AAA(Action<object> callBack)
            {
                callBack("てすと");
            }
    
            public void AAA()
            {
                var ce=CustomEvent;
                if(ce != null)
                {
                    ce(this,EventArgs.Empty);
                }
            }
    
            public event EventHandler<EventArgs> CustomEvent;
        }
    }
    

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2018年12月21日 5:34

すべての返信

  • C++言語の場合、ヘッダーファイルが存在すればコンパイルを行うことができ、その後のリンクのステップで結合が行われます。ところがC#言語の場合、コンパイルを行う段階でアセンブリ(≒EXEやDLL)が存在している必要があります。

    今回の質問は、C#側からC++の関数の呼び出しを行うことそのものよりも、相互参照の問題と思われます。解決策はいくつかありますが、それよりも設計を改め、相互参照を解消することをお勧めします。例えば、C#から呼び出したいC++部分をDLLとして分離し先にコンパイルするなど。

    2018年12月21日 2:10
  • 回答ではありません。やや疑問に思ったので質問させてください。
    真に
    ・DLL側から、EXEに実装してある関数を実行したい
    のでしょうか。その目的も気になるところです。

    2018年12月21日 2:31
  • 仲澤@失業者様

    ご返信、ありがとうございます。

    そうです。DLL側からEXEに実装してある関数を実行したいです。

    C(EXE)→C#(DLL)→C(EXE)と実行したいです。

    C#側で実施した値をC++側の画面に反映する必要があるため、

    C#側でC++の関数を呼び出したいです。

    よろしくお願いいたします。

    2018年12月21日 2:39
  • 佐祐理様

    ご返信、ありがとうございます。

    最もなご意見だと思います。

    検討させていただきます。

    よろしくお願いいたします。

    2018年12月21日 2:44
  • 了解しました。残念ですが回答としては、

    ・そのような事はやるべきではありません。(ほぼ不可能と言ってもよい)

    となります。理由は色々とあるのですが、一つだけ上げると

    ・Windows OSはそのような動作を前提として設計されていない。

    からですね。
    >C(EXE)→C#(DLL)→C(EXE)と実行したいです。
    >C#側で実施した値をC++側の画面に反映する必要があるため、
    >C#側でC++の関数を呼び出したいです。

    この様な場合は、呼びたしたいEXEの機能を発現するために、DLL側はEXEに対して

    (a)戻り値(構造でも可)に意味を持たせる。
    (b)実行したい意味のメッセージを送付する。

    等の方法をとることが多いです。
    OS自体もその方法を多く使用しています。

    2018年12月21日 2:57
  • ・そのような事はやるべきではありません。(ほぼ不可能と言ってもよい)

    ここは賛同します。

    ・Windows OSはそのような動作を前提として設計されていない。

    一応、実現可能なように設計されています。

    まず、質問の.NETアセンブリであればEXEもDLLも同じ形式であり、DLLからEXEの呼び出しは普通に可能です。また、相互参照そのものも解決不可能なわけではありません。例えばSystem.dllはXMLファイル操作のためにSystem.XML.dllを参照していますが、System.XML.dllももちろんSystem.dllを参照しています。

    ちなみにネイティブでもDLLがEXEを参照可能です。例えばnetsh.exeを拡張する際、RegisterContext関数で登録するDLLを作成しますが、この関数自身はnetsh.exeに含まれています。

    2018年12月21日 4:21
  • C#から呼ばせたい関数をデリゲートで渡して呼ばせれば普通にできますが…

    #MFCやWPFを用意するのが面倒なのでC++/CLIコンソールアプリと単純なC#DLLで

    #include "stdafx.h"
    #include <locale>
    #include <iostream>
    #include <vcclr.h>
    #include <tchar.h>
    using namespace System;
    
    void CallBacked(System::Object^ param)
    {
    	if (param != nullptr)
    	{
    		System::String^ str = param->ToString();
    		pin_ptr<const wchar_t> ps= PtrToStringChars(str);
    		const wchar_t* pc = ps;
    		std::wcout << pc << std::endl;
    	}
    }
    
    void EventCallback(System::Object^ sender, System::EventArgs^ e)
    {
    	if (e != nullptr)
    	{
    		System::String^ str = e->ToString();
    		pin_ptr<const wchar_t> ps = PtrToStringChars(str);
    		const wchar_t* pc = ps;
    		std::wcout << pc << std::endl;
    	}
    }
    
    int main(array<System::String ^> ^args)
    {
    	std::locale::global(std::locale(""));
    
    	System::Action<System::Object^>^ callBack = gcnew System::Action<System::Object^>(&CallBacked);
    
    	System::EventHandler<System::EventArgs^>^ handler = gcnew System::EventHandler<System::EventArgs^>(&EventCallback);
    
    	ClassLibrary1::Class1^ c = gcnew ClassLibrary1::Class1();
    	c->AAA(callBack);
    		
    	c->CustomEvent += handler;
    	c->AAA();
    
        return 0;
    }
    C#
    using System;
    
    namespace ClassLibrary1
    {
        public class Class1
        {
            public void AAA(Action<object> callBack)
            {
                callBack("てすと");
            }
    
            public void AAA()
            {
                var ce=CustomEvent;
                if(ce != null)
                {
                    ce(this,EventArgs.Empty);
                }
            }
    
            public event EventHandler<EventArgs> CustomEvent;
        }
    }
    

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2018年12月21日 5:34
  • 佐祐理さん、
    お教えいただいた上に質問になってしまい恐縮なのですが、
    その方法は、本件の要件である、呼び出し元の実行中のEXEのインスタンス内の関数を実行する
    ということが可能なのでしょうか。
    質問者さんはその方法を求めているようです。
    2018年12月21日 5:59
  • gekkaさん、
    私も頭をよぎりましたが、あえて割愛しました。
    というのも、「(b)メッセージを送付する」ということは、
    実質的にDLL関数の呼び元のウィンドウのコールバック関数を実行するということであって、
    かつ、推奨されている安全な方法です。
    それに比べてコールバックされる関数を引数で渡すことに、
    特別なメリットがあるとは思えませんでした。
    個人的には、単にできるというだけでは推薦するには抵抗感が強すぎました。

    2018年12月21日 6:02
  • 可能だと思いますよ。ただし、やるべきでないという立場ですので、手順を紹介するつもりもありませんし、紹介のために手順を調べる気もありません。
    # Facade Projectでも作るとか?
    2018年12月21日 6:26
  • gekka様、仲澤@失業者様、佐祐理様

    ご返信いただき、ありがとうございます。

    ご教示いただいた内容をもとに、再度検討したいと思います。

    ありがとうございました。

    2018年12月21日 7:26