スレッドは生成・破棄に時間がかかる(下手すりゃ秒単位)。そこでスレッドプール等の運用方法が考案され、効率化が図られてきた。
しかしそれらを実装するのは結構な手間で、プログラマが一々手を煩わせるべきではない。
そのような要望から生まれたのがTaskで、効率的なスレッドの運用を内部で勝手にやってくれる。
という事らしい。
参考文献
https://stackoverflow.com/questions/31531697/c-cli-using-iprogress-in-task
https://teratail.com/questions/82895
using namespace System; //パラメータ受け渡し用のクラス public ref class CParam { public: int i; }; //一つのタスクとなるクラス public ref class MyClass { CParam^ param; System::Threading::Tasks::Task^ myTask; public: MyClass(int k) { param = gcnew CParam; param->i = k; } void TaskMethod(Object^ obj);//スレッドで走る関数 void Start(); void Wait(); }; //スレッドを開始する関数 void MyClass::Start() { myTask = System::Threading::Tasks::Task::Factory->StartNew( gcnew System::Action<Object^>(this, &MyClass::TaskMethod), (Object^)param); } //終了まで待機 void MyClass::Wait() { myTask->Wait(); } //処理本体 void MyClass::TaskMethod(Object^ obj) { CParam^ cp = (CParam^)obj; for (int j = 0; j < 100; j++) { Console::WriteLine(System::String::Format("{0}", cp->i + j)); } } ///////////////////////////////////////////////////// int main(array<System::String ^> ^args) { MyClass^ mc1 = gcnew MyClass(100); MyClass^ mc2 = gcnew MyClass(200); MyClass^ mc3 = gcnew MyClass(300); mc1->Start(); mc2->Start(); mc3->Start(); mc1->Wait(); mc2->Wait(); mc3->Wait(); Console::Read(); return 0; }
ちょっと自分でも何言ってるのかわからない。
以下のような状態で、(article)の記事で、
categoryB
CAT_B
cat_b1
cat_b2
CAT_BB
cat_bb1
cat_bb2
という一覧を出したい。rootまで遡らず、categoryB以下のカテゴリ一覧だけを取得したいわけである。
root
categoryA
CAT_A
cat_a1
cat_a2
CAT_AA
cat_aa1
cat_aa2
categoryB
CAT_B
cat_b1
cat_b2
CAT_BB
cat_bb1
cat_bb2 (article)
categoryC
CAT_C
cat_c1
cat_c2
CAT_CC
cat_cc1
cat_cc2
原理は
①まず注目カテゴリとして、現在のページのカテゴリを取得 get_queried_object()
②そのカテゴリの親カテゴリを取得 $catobj->parent
③そのカテゴリが上記でいうところのcategoryBなら、wp_list_categoriesで一覧表示
④そうでないなら、注目カテゴリを②に更新して、②に戻る get_category
<?php $catobj = get_queried_object(); /*現在のカテゴリIDを取得*/ /*星の部屋カテゴリと、その下のカテゴリまでを取得*/ while(true){ $parentid = $catobj->parent; //注目中のカテゴリの親カテゴリを取得 if($parentid == 0 || $parentid == 121) //それが「ほしいカテゴリ階層のトップ」ならループを抜ける。(0判定は無限ループ対策) break; $catobj = get_category($parentid); //親カテゴリ取得。ない場合は0 //parentidに「ほしいカテゴリ階層のトップ」が入っていないなら、それを次の注目カテゴリに設定 } if( $parentid != 0 ){ //0の時は欲しいカテゴリ階層ではないということ(何かが間違っている)ので何もしない echo '<ul class="childofcategory">'; wp_list_categories('child_of='.$catobj->term_id.'&depth=0&hide_empty=0&title_li=');//リストはこの関数で$catobj->term_idを指定して取得 echo '</ul>'; } ?>
私はカテゴリを階層にすると絶対にPHPが面倒になると確信を持っていたので避けているのだが、そんなこととは全く関係ない☆は普通に階層分けしているので結局やる羽目になった。まあこっちの技術力がないからあれをしないでというのは何なので別にいいのだが。。。
以下はアマゾンギフト券(メール)を模したものである。このサイトでアフィリエイトを始めようと一念発起したわけだが、以前にもアフィリエイトに挑戦し、支払方法を選択してくださいの状態で5年だか10年位前に作って放置していたので、アマゾンギフト券を選択したところ意外なことにそこそこまとまった金額が届いた。
さて、この中で、私はギフト券番号を探すのに3分くらいかかった。金額も受取人名も有効期限も注文番号も、10秒以内に把握したが、ギフト券番号だけは最後までわからなかった。
なぜか。それがこの記事の本題である。
¥3,000のAmazonギフト券をお送りします。 |
|||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||
|
順を追ってみてみよう。
①まず、メールが届く。タイトルから、これがアマゾンギフト券であることを把握する。
②そこから、デジタルな金券の特性上、アルファベットないし数字あるいはその両方で構成された無機質な文字の並び(場合によってはそれがハイフンで区切られている)が必要になるので、恐らくそれがこのメールの中に書かれているであろうことが推測できる。
③メールを開ける。以下がその本文である。
a. ここで、まずエリアCを無視する。重要度はともかく、すぐに必要な情報が小さな字で書いてあるわけがないからだ。
b. 次に、エリアAを読み、必要な情報の呼称が「ギフト券番号」であることを確認する。
c. エリアBを流し読みし、保留にする。ギフト券番号がわからなけれは意味のない情報だ。
④ 上記アマゾンギフト券を見ながら、このメールで最も重要な情報は何か、を考える。まずわかるのは「アマゾンギフト券」のロゴだ。ここに情報は何もない。
⑤ そして次に書かれた情報が金額である。文字の大きさ的にも、位置的にも、目立つように書かれている。
⑥ 次に名前だ。自分の名前は自分にとって特別な情報であるから、殆ど自動的に注目するようになっている
⑦ その次が「Amazon Associates Program」の文字だ。「お受取人」「贈り主」の文字は薄い黄色で見づらく、その下の「メッセージ」は細字である。そして周囲は白い空間で囲まれているので、自分の名前で近くまで視線誘導されれば、次に目立つのがこの部分なのだ。
⑧ さて。簡単に得られる情報はこれで終わりなので、ギフト券番号を探さなければならない。
ここでギフト券番号はとても重要な情報なので、大きな文字か、太字(濃い字)で書かれている可能性が高い。
⑨まず大きな字を探す。3000円の表記が見つかるが、これより大きい字はない。ギフト券番号は金額より遥かに重要な情報なので(金額は買い物までに分かればいいが、ギフト券番号がわからないとそもそもそのステップへ行けない)、少なくとも金額と同じサイズかそれ以上の大きさで書かれているはずだ。ところがそんな文字列はない。つまり、ギフト券番号は大きな文字で書かれてはいないことが予測できる。
⑩ となると太字で書かれている可能性である。探すと自分の名前と贈り主、そして
買い物をする
Amazonギフト券をアカウントに追加する
Amazonギフト券の使い方
という最下部の項目しか該当しない。つまり、ギフト券番号は大きな文字で書かれてもいないし、太字でもないという事になる。ここまでをまとめると、ギフト券番号の特徴は
●普通のフォントかつ普通以下の文字サイズ
●数字、アルファベット、ハイフンの何れかで構成されている
●そこそこ長い文字列
であることが推測できる。
⑪アルファベットあるいは数字を探すと、2028/06/18が見つかる。次に注文番号が見つかり、これかとも思うが、「ギフト券番号」ではない。後は上の方の「¥3,000のAmazonギフト券をお送りします。~」の文章の中に書いてあるかとも思ったが違う。残るはギフト券中央、下線付きの「Amazonギフト券のご利用方法」という項目だが、これも違う。するとこの中にはギフト券番号が書かれていないといことになり、後可能性があるのは「Amazonギフト券のご利用方法」から始まるメール中の説明文だけになる。
⑫説明文A,Bを読むが、(当然だが)書いていない。やはり上のカードを模した画像の中にあると考える方が妥当だ。
⑬仕方がないので読みづらい黄色い文字を読む。「金額」まあ違うと分かっていても一つ一つ確認するのである。そして「ギフト券番号」を見つける。なんと金額より小さく、名前よりも薄い字で書かれていたのである。わからないわけだ。
視野が狭いというのは、普通言う所の「対象がボケて見える」とは勝手が違う。情報の内容がわからない以前に、情報の場所がわからないのだ。だから常に経験から「どこに、どんな形で」情報があるかを予測し、それに基づいて捜索を行っている。
逆に言えば予測が外れた場合には情報を発見できない。大きいとかいった特徴は関係なく、「予想通りの格好をしているか」だけが重要なのである。
目立つというのは、広い視野でもって「広く浅く全体の状態を把握したときに、ある一か所がその他と比較して異質であれば、そこに注意がいく」という現象である。
つまり一塊の情報からの絞り込みに有効なのであって、そもそも一度にとらえることのできる情報が少ない場合には役に立たないのだ。
とはいえ現実にはここまで極論を考える必要はない(少なくとも私の場合は、だが)。なぜかというと文章に対する姿勢がまずページのある領域(面)→文章(線)→文字(点)という順番で意識を集中していくこと自体は恐らく同じだからだ。
そうすると、文字を大きくするというのが無効だという事がわかる。なぜか?大きくなればなるほどそれは「面」に近くなるからだ。点を可能な限り点のままで強調する方法を考えると、必然的に色や太さといったところに落ち着くのである。
テキストファイルの書き込みにはStreamWriterを用いる。System::Text::Encodingでエンコーディングの指定が可能
void FileWrite(System::String^ filepathname) { System::IO::StreamWriter^ sw = gcnew System::IO::StreamWriter( filepathname, //ファイル名 false, //追記モード System::Text::Encoding::UTF8 //System::Text::Encoding::GetEncoding("shift_jis") //文字コード ); sw->Write("吾輩は猫である。名前はまだ無い。\n"); sw->Write("どこで生れたかとんと見当がつかぬ。\n"); sw->Write("何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。\n"); sw->Write("吾輩はここで始めて人間というものを見た。\n"); sw->Write("しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。\n"); sw->Write("この書生というのは時々我々を捕えて煮て食うという話である。\n"); sw->Close(); }
テキストファイルの読み込みにはStreamReaderを使用する。
一行ずつの読み込みにはReadLineを用いる。ファイル終端判定はPeekを使うのが一般的のようだが、参考文献によると模範解答ではないらしい。ここではEndOfStreamを使っている
void FileRead(System::String^ filepathname) { //"C:\test\1.txt"をShift-JISコードとして開く System::IO::StreamReader^ sr = gcnew System::IO::StreamReader( "C:\\test\\sample.txt", System::Text::Encoding::Encoding::UTF8); while(sr->EndOfStream != true ){ System::String^ r = sr->ReadLine(); System::Console::WriteLine( r );//ReadLineは改行を含まない } sr->Close(); }
int main(array<System::String ^> ^args){ FileWrite("C:\\test\\sample.txt"); FileRead("C:\\test\\sample.txt"); System::Console::Read(); }
参考文献
https://dobon.net/vb/dotnet/file/readfile.html
グローバルフックをするには、DLLにする必要がある。
また、イベントがあったことを知るために、FindWindowで送信先のウィンドウクラス名を指定してSendMessageしている。
マウスフックの時は、SetWindowsHookExに渡す引数はWH_MOUSEとなる。また、フックプロシージャのwParamにはイベントが入っている。
#pragma once #include <Windows.h> BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ); HINSTANCE GetDllInstance();
HINSTANCE hDLLInstance = nullptr; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { hDLLInstance = hModule; return TRUE; } HINSTANCE GetDllInstance() { return hDLLInstance; }
フックをかけるときにインスタンスハンドルが必要になるので、DllMainの中で保存しておく。
DLL側にはプリプロセッサにDLL_EXPORT_DOをセットする。
#ifdef DLL_EXPORT_DO /* DLLを作る場合 */ #define __DLL_PORT extern "C" __declspec(dllexport) #define __DLL_PORT_CLS __declspec(dllexport) #else /* DLLを使う場合 */ #define __DLL_PORT extern "C" __declspec(dllimport) #define __DLL_PORT_CLS __declspec(dllimport) #endif
このファイルは次のMouseHook.hでincludeされるが、結果的にexe側でもincludeされることになるので、両方のプロジェクトから読める必要がある。
#pragma once #include <Windows.h> #include "../DLLDefs.h" //マウスがクリックされたら、本体のプログラムへこのメッセージを送信する constexpr UINT WM_HOOKED_MOUSEDOWN = WM_USER + 2; class __DLL_PORT_CLS CMouseHook { static HHOOK hHook; static LRESULT CALLBACK MouseProcedure(int p_nCode, WPARAM p_wParam, LPARAM p_lParam); public: BOOL Set(); BOOL Unset(); ~CMouseHook(); };
フック関数とフックハンドルはstaticにする。
#include "MouseHook.h" #include "dllmain.h" #include <tchar.h> #pragma warning(disable:4996) #pragma data_seg( ".CMouseHookHandle" ) HHOOK CMouseHook::hHook = nullptr; #pragma data_seg() BOOL CMouseHook::Set() {//フックをセットする関数 hHook = ::SetWindowsHookEx(WH_MOUSE //種類はマウスフック , (HOOKPROC)CMouseHook::MouseProcedure , GetDllInstance(), 0);//このDLLのインスタンスハンドルを指定 if (!hHook) return FALSE; return TRUE; } BOOL CMouseHook::Unset() {//フックを外す BOOL ret = FALSE; if (hHook) ret = ::UnhookWindowsHookEx(hHook); hHook = nullptr; return ret; } //フックの処理 LRESULT CALLBACK CMouseHook::MouseProcedure(int p_nCode, WPARAM p_wParam, LPARAM p_lParam) { if (p_nCode < 0) return ::CallNextHookEx(hHook, p_nCode, p_wParam, p_lParam); if (p_nCode == HC_ACTION) { MOUSEHOOKSTRUCT *pmh = (MOUSEHOOKSTRUCT *)p_lParam; if (pmh) { if (p_wParam == WM_LBUTTONDOWN || p_wParam == WM_NCLBUTTONDOWN) { //このNU_WND_CLASS_NAMEは、exe側で作成するウィンドウのウィンドウクラス名 PostMessage(FindWindow(_T("NU_WND_CLASS_NAME"), nullptr), WM_HOOKED_MOUSEDOWN, p_wParam, p_lParam); //exe側にWM_HOOKED_MOUSEDOWNを送る } } } return ::CallNextHookEx(hHook, p_nCode, p_wParam, p_lParam);; } CMouseHook::~CMouseHook() { Unset(); }
SECTIONS .CMouseHookHandle READ WRITE SHARED
#pragma comment(lib, "MouseHook.lib") #include "../MouseHook/MouseHook.h" #include<windows.h> #include <tchar.h> //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { switch (msg) { case WM_DESTROY: DestroyWindow(hwnd); PostQuitMessage(0); return 0; case WM_HOOKED_MOUSEDOWN: OutputDebugString(_T("どこかの窓でマウスがクリックされました\n")); return 0; } return DefWindowProc(hwnd, msg, wp, lp); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG msg; WNDCLASS winc; //ウィンドウクラスの登録 winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL, IDI_APPLICATION); winc.hCursor = LoadCursor(NULL, IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("NU_WND_CLASS_NAME"); if (!RegisterClass(&winc)) return 0; //フックをかける CMouseHook hm; hm.Set(); //ウィンドウ作成 hwnd = CreateWindow( TEXT("NU_WND_CLASS_NAME"), TEXT("test"), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 200, 200, NULL, NULL, hInstance, NULL ); if (hwnd == NULL) return 0; while (GetMessage(&msg, NULL, 0, 0)) DispatchMessage(&msg); //フックを外す hm.Unset(); return msg.wParam; }
VRAY for Blenderでfogを使う方法。
オブジェクトのマテリアルではなく、Worldの方のEffectsにフォグを設定し、「FogをObjectの中限定で発生させる」という考え方をするらしい。
複数のオブジェクトをフォグにする場合、Effects ContainerにAddを使い、二つ以上の設定を束ねてWorld Outputへ接続する。
CUDAをやってみたくなった。
インストールがもの凄く簡単になっていて、Visual C++ 2017 Communityが入っているなら
https://developer.nvidia.com/cuda-downloads
からCUDA Toolkitをダウンロードしてインストールするだけで、VC++のプロジェクトにCUDAのプロジェクトが追加される。
以下、初めてのCUDAプログラム
#include "cuda_runtime.h" #include "device_launch_parameters.h" #include <stdio.h> #include <cstring> __global__ void hello(char *c) { c[0] = 'h'; c[1] = 'e'; c[2] = 'l'; c[3] = 'l'; c[4] = 'o'; c[5] = '\0'; } int main(void) { const size_t LEN = strlen("hello") + 1;//(CPU側) char c_cpu[LEN];//(CPU側) char *c_gpu;//(CPU側) cudaMalloc( (void**)&c_gpu, LEN);//GPU側にメモリを確保 hello <<<1, 1 >>>( c_gpu );//GPU側の関数を呼出 cudaMemcpy(&c_cpu, c_gpu, LEN, cudaMemcpyDeviceToHost);//GPU側から実行結果を取得 cudaFree(c_gpu);//GPU側のメモリを解放 puts(c_cpu);//(CPU側) getchar();//(CPU側) return 0; }
cudaMalloc
GPU側のメモリを確保するための関数。
関数名 | cudaMalloc | ||||||
戻り値 | cudaError_t | ||||||
引数 |
|
参考URI:
cudaFree
cudaMallocで確保したメモリを開放する
関数名 | cudaFree | |||
戻り値 | cudaError_t | |||
|
参考URI
特に解説することはない。
ref class CTest{ public: int func(double v){ return (int)v * (int)v; } delegate int DelegateToFuncT(double v);//デリゲート型を定義 }; /////////////////////////////////////////////////// int main(array<System::String ^> ^args) { CTest^ ct = gcnew CTest(); //デリゲート作成 CTest::DelegateToFuncT^ dfunc = gcnew CTest::DelegateToFuncT(ct,&CTest::func); int ret = dfunc( 5.5 );//呼出 Console::WriteLine( System::String::Format("** {0}\n",ret) ); Console::ReadLine(); return 0; }
#pragma onceを忘れたとかそんな話ではない。
すべてCLIで、testclidll.dll内にクラスClass1を作成し、それをexe側に参照を追加してClass1を使おうとしたとき、
error C2011: 'testclidll::Class1' : 'class' 型の再定義
が発生する
再現手順 (Visual Studio 2017 community)
1.[ファイル] - [新規作成] - [プロジェクト] から、「CLRコンソールアプリケーション」を作成(名前をexe_and_dll_testとする)
2.[ファイル] - [新規作成] - [プロジェクト] から、「クラスライブラリ」を作成(名前をtestclidllとする)
3.testclidll.hを以下のようにする
// testclidll.h #pragma once using namespace System; namespace testclidll { public ref class Class1 { int c; public: void Set(int d); int Get(); }; }
4.testclidll.cppを以下のようにする
namespace testclidll { void Class1::Set(int d){ c = d; } int Class1::Get(){ return c; } }
5.exe側を以下のようにする
#include "stdafx.h" using namespace System; #include "../testclidll/testclidll.h" int main(array<System::String ^> ^args) { testclidll::Class1^ c = gcnew testclidll::Class1; c->Set(10); Console::WriteLine(System::String::Format("{0}\n",c->Get())); return 0; }
6.DLLをコンパイルする
7.ソリューションエクスプローラから、exe側のプロジェクトを右クリックし、[参照...]を選択
プロパティページから、[共通プロパティ]-[Frameworkと参照]で「新しい参照の追加...」ボタンを押し、コンパイルしたdllを追加する
8.exe側をコンパイルする
2>e:\mycodes\exe_and_dll_test\exe_and_dll_test\../testclidll/testclidll.h(12): error C2011: 'testclidll::Class1' : 'class' 型の再定義
2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
2>exe_and_dll_test.cpp(14): error C2027: 認識できない型 'testclidll::Class1' が使われています。
2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
2>exe_and_dll_test.cpp(14): error C2227: '->Set' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
2>exe_and_dll_test.cpp(18): error C2027: 認識できない型 'testclidll::Class1' が使われています。
2> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
2>exe_and_dll_test.cpp(18): error C2227: '->Get' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
まず、C/C++ではdllをリンクするときにも必ずヘッダファイルをincludeしなければならないが、.NETではdllが情報を持っている(?)ので、そこへさらにインクルードをかけると定義が二重に行われる(のだと思う)。従って、
#include "stdafx.h" using namespace System; //#include "../testclidll/testclidll.h" //このincludeはしてはいけない int main(array<System::String ^> ^args) { testclidll::Class1^ c = gcnew testclidll::Class1; c->Set(10); Console::WriteLine(System::String::Format("{0}\n",c->Get())); return 0; }
再現手順
1. testclidll.hを、以下のように修正する
// testclidll.h #pragma once using namespace System; namespace testclidll { enum ReturnT{ //型を追加 True,False, }; public ref class Class1 { int c; public: void Set(int d); int Get(); ReturnT isOdd(); //メンバが奇数かどうかを返す関数を追加。戻り値はユーザー定義型のReturnT }; }
2.testclidll.cppを以下のように修正する
// これは メイン DLL ファイルです。 #include "stdafx.h" #include "testclidll.h" namespace testclidll { void Class1::Set(int d){ c = d; } int Class1::Get(){ return c; } //メンバが奇数かどうかを返す関数の定義 ReturnT Class1::isOdd(){ if( c % 2 != 0 ) return True; return False; } }
3.exe側を以下のように修正する
#include "stdafx.h" using namespace System; //#include "../testclidll/testclidll.h" int main(array<System::String ^> ^args) { testclidll::Class1^ c = gcnew testclidll::Class1; c->Set(10); testclidll::ReturnT t = c->isOdd();//奇数かどうかの判定を追加 Console::WriteLine(System::String::Format("{0}\n",c->Get())); return 0; }
4.testclidllをコンパイルする
5.exe側をコンパイルする
コンバイルエラー
2>exe_and_dll_test.cpp(16): error C2039: 'ReturnT' : 'testclidll' のメンバーではありません。
2>exe_and_dll_test.cpp(16): error C2065: 'ReturnT' : 定義されていない識別子です。
2>exe_and_dll_test.cpp(16): error C2146: 構文エラー : ';' が、識別子 't' の前に必要です。
2>exe_and_dll_test.cpp(16): error C2065: 't' : 定義されていない識別子です。
これは、クラスの情報は参照したdllの中にあるが、enumのような定数の情報は入っていない(?)ので、定義がないと怒られる。そこで、testclidll.hのincludeを復活させてみる。
#include "stdafx.h" using namespace System; #include "../testclidll/testclidll.h" //やっぱりいるのか? int main(array<System::String ^> ^args) {
1>e:\mycodes\exe_and_dll_test\exe_and_dll_test\../testclidll/testclidll.h(14): error C2011: 'testclidll::Class1' : 'class' 型の再定義
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(14): error C2027: 認識できない型 'testclidll::Class1' が使われています。
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(14): error C2227: '->Set' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
1>exe_and_dll_test.cpp(16): error C2027: 認識できない型 'testclidll::Class1' が使われています。
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(16): error C2227: '->isOdd' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
1>exe_and_dll_test.cpp(18): error C2027: 認識できない型 'testclidll::Class1' が使われています。
1> e:\mycodes\exe_and_dll_test\debug\testclidll.dll : 'testclidll::Class1' の宣言を確認してください。
1>exe_and_dll_test.cpp(18): error C2227: '->Get' : 左側がクラス、構造体、共用体、ジェネリック型へのポインターではありません。
当然のように最初のエラーが再発する。
enumの定義を別のヘッダファイルへ移動する
testclidllプロジェクト内、defvalheader.hを追加
namespace testclidll { enum ReturnT{ True,False, }; }
それに伴い、dll本体側であるtestclidll.hからenumの定義を消し、includeにする
// testclidll.h #pragma once #include "defvalheader.h" //ReturnTの定義を読み込む using namespace System; namespace testclidll { public ref class Class1 { int c; public: void Set(int d); int Get(); ReturnT isOdd(); }; }
exe側では、testclidll.hではなく、defvalheader.hを読み込む
#include "stdafx.h" using namespace System; #include "../testclidll/defvalheader.h" int main(array<System::String ^> ^args) { testclidll::Class1^ c = gcnew testclidll::Class1; c->Set(10); testclidll::ReturnT t = c->isOdd(); Console::WriteLine(System::String::Format("{0}\n",c->Get())); return 0; }