2005年07月26日
川俣晶の縁側ソフトウェア技術雑記total 3395 count

Managed C++で記述したMixedコードに含まれるnon GCなnewが、NUnitでテストすると失敗する問題

Written By: 川俣 晶連絡先

 Visual Studio 2003を使用し、Managed C++で記述したManaged/UnmanagedのMixedコードをNUnitでテストすると失敗するケースがあります。

 具体的には、GC対象ではないクラスに対するnewや、昔懐かしいmallocで発生することを確認しました。

 再現コードを以下に示します。

再現コード §

 Visual Studio 2003を使用し、C++のWin32、DLLのプロジェクトを作成します。

 プロジェクトのプロパティからマネージ拡張を「はい」に変更します。

 参照にnunit.framework.dllを追加します。

 その後、メインソースコードとなるcppファイルを以下のように記述します。

#include "stdafx.h"

#using <mscorlib.dll>

#include <malloc.h>

BOOL APIENTRY DllMain( HANDLE hModule, 

                       DWORD  ul_reason_for_call, 

                       LPVOID lpReserved

                     )

{

    return TRUE;

}

using namespace System;

using namespace NUnit::Framework;

namespace NUnitAllocFailTest

{

    class NonGCSample

    {

    };

    [TestFixture]

    public __gc class TestSample

    {

    public:

        [Test] void Test1()

        {

            NonGCSample * p = new NonGCSample();

        }

        [Test] void Test2()

        {

            void * p = malloc(100);

        }

    };

}

 これをビルドし、NUnit 2.0のNUnit-GUIで実行すると例外で落ちます。

 Visual Studio.NET 2003内でTest Driven.NETを用いてテスト実行すると以下のような結果を得ます。

------ Test started: Assembly: NUnitAlloc002.dll ------

TestCase 'NUnitAllocFailTest.TestSample.Test1' failed: System.NullReferenceException : オブジェクト参照がオブジェクト インスタンスに設定されていません。

at new(UInt32 )

d:\w\test\nunitalloc002\nunitalloc002.cpp(46,0): at NUnitAllocFailTest.TestSample.Test1()

TestCase 'NUnitAllocFailTest.TestSample.Test2' failed: System.NullReferenceException : オブジェクト参照がオブジェクト インスタンスに設定されていません。

at malloc(UInt32 )

d:\w\test\nunitalloc002\nunitalloc002.cpp(50,0): at NUnitAllocFailTest.TestSample.Test2()

0 succeeded, 2 failed, 0 skipped, took 0.09 seconds.

---------------------- Done ----------------------

原因 §

 動作を追いかけてみましたが、どうもnewの下請け関数からクリティカルセクションに入るあたりに問題があるような気がしますが、定かではありません。厳密には追求していません。

安易な解決 §

 とりあえず、安易ではありますが、以下のようにすればnewの問題は解決します。

 Win32 APIのLocalAlloc呼び出しが問題ないことは確認済みなので、以下のコードを追加して、newとdeleteをオーバーライドします。

void * operator new( size_t stAllocateBlock )

{

    void *pvTemp = (void*)LocalAlloc( LMEM_FIXED, stAllocateBlock ); 

    /* えっと、以下の2行はいるのかな? サンプルソースから安易に引いてきたけれど */

    if( pvTemp != 0 )

        memset( pvTemp, 0, stAllocateBlock );

    return pvTemp;

}

void operator delete( void *pvMem )

{

    LocalFree( (HLOCAL)pvMem );

}

 しかし、この方法でもmallocの問題は解決しません。

 もし、mallocを使う必要があるなら、malloc/freeもユーザープログラム側で置き換えて使う必要があるかもしれません。

未完 §

 この内容は未完です。これが完全な解決であるとか、問題の解消というわけではありません。

 関連する情報等は歓迎します。

Facebook

このコンテンツを書いた川俣 晶へメッセージを送る

[メッセージ送信フォームを利用する]

メッセージ送信フォームを利用することで、川俣 晶に対してメッセージを送ることができます。

この機能は、100%確実に川俣 晶へメッセージを伝達するものではなく、また、確実に川俣 晶よりの返事を得られるものではないことにご注意ください。

このコンテンツへトラックバックするためのURL

http://mag.autumn.org/tb.aspx/20050726233448
サイトの表紙【技術雑記】の表紙【技術雑記】のコンテンツ全リスト 【技術雑記】の入手全リスト 【技術雑記】のRSS1.0形式の情報このサイトの全キーワードリスト 印刷用ページ

管理者: 川俣 晶連絡先

Powered by MagSite2 Version 0.36 (Alpha-Test) Copyright (c) 2004-2021 Pie Dey.Co.,Ltd.