概要‎ > ‎

ネイティブDLL公開関数の呼び出し

以下のサンプルはこちらからダウンロードできます。

今までの解説は.Netの操作を呼び出すものでした。
では、ネイティブアプリケーションはどうすればよいでしょうか?

FriendlyOperationは.Netのフィールド、プロパティー、メソッドを呼び出すものなので、ネイティブDLL関数を直接は指定できません。
しかし、前項で説明したDLLインジェクションと合わせて使うとネイティブDLL公開関数を呼び出すことができます。



この例では、プロダクトプロセスは空のWin32プロジェクトを使います。
作成したままの状態でコードは何も書き加えません。
テストプロセスのコードです。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Codeer.Friendly.Windows;
using Codeer.Friendly.Dynamic;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace TestProcess
{
    [
TestClass]
   
public class NativeDllInjectionSample
    {
#if DEBUG
       
const string Mode = "Debug";
#else
        const string Mode = "Release";
#endif
       
internal const string NativeProcessPath = @"..\..\..\" + Mode + @"\NativeProcess.exe";

       
WindowsAppFriend _app;
       
Process _process;

        [
TestInitialize]
       
public void TestInitialize()
        {
           
//プロダクトプロセスを起動し、接続する。
            _app =
new WindowsAppFriend(Process.Start(NativeProcessPath));
            _process =
Process.GetProcessById(_app.ProcessId);
        }

        [
TestCleanup]
       
public void TestCleanup()
        {
            _app.Dispose();
            _process.CloseMainWindow();
        }

        [
TestMethod]
       
public void TestRect()
        {
           
//自身のアセンブリをプロダクトプロセスにも読み込ませる。
           
WindowsAppExpander.LoadAssemblyFromFile(_app, GetType().Assembly.Location);

           
//ウィンドウを移動。
            _app.Type<
NativeDllInjectionSample>().MoveWindow(_process.MainWindowHandle, 0, 0, 200, 200, true);

           
//ウィンドウ矩形を取得。
           
dynamic rectInTarget = _app.Type<RECT>()();
            _app.Type<
NativeDllInjectionSample>().GetWindowRect(_process.MainWindowHandle, rectInTarget);
           
RECT rect = (RECT)rectInTarget;

           
//評価。
           
Assert.AreEqual(0, rect.left);
           
Assert.AreEqual(0, rect.top);
           
Assert.AreEqual(200, rect.right);
           
Assert.AreEqual(200, rect.bottom);
        }

       
//プロダクトプロセスでロードされ、実行されます。
        [
DllImport("User32.dll")]
       
static extern bool MoveWindow(IntPtr handle, int x, int y, int width, int height, bool redraw);

       
//プロダクトプロセスでロードされ、実行されます。
        [
DllImport("user32.dll")]
        [
return: MarshalAs(UnmanagedType.Bool)]
       
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
    }

    [
Serializable]
    [
StructLayout(LayoutKind.Sequential)]
   
internal struct RECT
    {
       
public int left;
       
public int top;
       
public int right;
       
public int bottom;
    }
}


User32.dllの公開関数を呼び出しています。
FriendlyOperationは.Netのフィールド、プロパティー、メソッドを呼び出すものなので、ネイティブDLL関数を直接は指定できません。
そのため、一度.Netのクラスにロードしてそれを呼び出すわけです。
ロードはプロダクトプロセス内で実施する必要があるので、WindowsAppExpanderを使います。
この説明はプロダクトプロセスの拡張の項を参照してください。

もちろん、システムのDLL関数だけではなく、ユーザー定義のDLL公開関数も呼び出せます。

先ほどのWin32アプリケーションに次のコードを付け加えてみてください。

//Exeでも公開関数を定義することは可能です。
extern "C" __declspec(dllexport) int __cdecl TestFunction()
{
        return 100;
}


TestFunctionをテストコードから呼び出してみます。

//テストコード。
using System;
using System.Diagnostics;
using Codeer.Friendly;
using Codeer.Friendly.Windows;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Runtime.InteropServices;
 
namespace TestProcess
{
    [TestClass]
    public class NativeSample
    {

#if DEBUG
        
const string Mode = "Debug";
#else
        const string Mode = "Release";
#endif
        
internal const string NativeProcessPath = @"..\..\..\" + Mode + @"\NativeProcess.exe";

        [TestMethod]

        public void TestDllFunction()
        {
            Process process = Process.Start(NativeProcessPath);
            using (WindowsAppFriend app = new WindowsAppFriend(process))
            {
                //自身のアセンブリをプロダクトプロセスにも読み込ませる。

                
WindowsAppExpander.LoadAssemblyFromFile(app, GetType().Assembly.Location);
                //MFCProductProcess.exeに定義されたDLL公開関数を実行。
                int result = (int)app[GetType(), "TestFunction"]().Core;
                //評価。
                Assert.AreEqual(100, result);
            }
            process.CloseMainWindow();
        }

        //プロダクトプロセスでロードされて実行される。
        [DllImport("NativeProcess.exe")]
        static extern int TestFunction();
    }
}


Exeでも関数を公開することは可能です。
P/Invokeのルールに関しては一般的な情報なので、Webで検索お願いします。

.Netはすべての操作を呼び出すことが可能ですが、
ネイティブはDLL公開した関数しか呼び出せません。
現実的にはいくつかはテスト用に公開関数を仕込んでおくことになると思います。

また、コントロール操作ではWindowsAPI(User32,dllとKernel32.dll)を使えば、多くの操作が可能ですが、
テストのたびにその操作を記述するのは面倒です。
また、汎用的なのでネイティブアプリケーションをテストする場合は、多くの箇所で使います。
そのため、弊社でその操作をラッピングしたライブラリを提供しています。


合わせて、使用してください。

ところで、今までは、基本の説明ということでウィンドウ操作を意図的に抜いていいたのですが、
実際に結合テストを記述する場合は、ウィンドウ操作がかなり入ってきます。
次項ではそれを説明させて頂きます。