質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

4598閲覧

DLLのクラスオブジェクト操作ができない...

BeatStar

総合スコア4958

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2016/06/30 04:30

C/C++でやっています。
基本的には MinGW (g++) でやっているのですが、(C/C++側で)IE操作やExcel操作もやってみたいと思い、
VC++でIE,Excel用のそれぞれのDLLを作成しています。

質問は2つほどあります。

Q1.
IE操作用 インターフェースクラス ( たとえば IInternetExplorer ) を定義し、
それを継承して 実際に動くクラス ( たとえば CInternetExplorer ) を定義し、
FactoryMethodパターンを適用して CreateInstance関数 でオブジェクトを生成しています。

C++

1 // VC++ でのDLL作成 2 3 // ここでタイプライブラリの取得(?) 4 // ... 5 6 // インターフェースクラス 7 class IInternetExplorer 8 { 9 public: 10 virtual ~IInternetExplorer(){} 11 12 virtual bool Run( bool bVisible ) = 0; // IE起動 13 virtual bool Open( char *url ) = 0; // 14 ... 15 }; 16 17 // 実際に動くクラス 18 class CInternetExplorer : public IInternetExplorer 19 { 20 public: 21 CInternetExplorer(); 22 ~CInternetExplorer(); 23 24 bool Run( bool bVisible ); // IE起動 25 bool Open( char *url ); // url のところに... 26 ... 27 }; 28 29 IInternetExplorer* CreateInstance( bool bVisible ); 30

(一応、メンバ関数定義ありますが、省略...)

それで、

MinGW 側の実行ファイルでは (Windows APIの)動的リンクでリンクしています。

(もちろん、インターフェースクラスも定義した状態で.) CreateInstanceで
オブジェクトを生成するところまではうまくいくのですが、
なぜか 実際にオブジェクトを操作 ( Run関数とか ) をするとクラッシュ(?)します。

考えた末、

DLLでFactoryMethodパターンを適用し生成 -> DLLの関数にオブジェクトを渡して間接的に操作...

にたどり着きました。

つまり、

DLLでは

■ インターフェースクラス
■ 実装クラス
■ CreateInstance ( FactoryMethodパターン適用で インターフェースクラスオブジェクトを返す )
■ RunIE関数 ( グローバル関数. 引数の インターフェースクラス を受け取り、代理で動かす. )
■ OpenIE関数 ( グローバル関数. 引数の インターフェースクラス を受け取り、代理で動かす. )
...

みたいにして、

実行ファイル側はオブジェクトをいじらないでそのまま RunIE関数等に渡して...

という感じです。

動くことは動くのですが、
なぜ実行ファイルで そのまま動かそうとするとクラッシュ状態になるのでしょうか。
実体は生成されているはずなのに...

理由がわからない...

もっとほかにいい方法があるのでしょうか。

外部ライブラリに頼る方法もありますが、
Windowsだとmakeが使えないようだし、
libxl っていうライブラリ ( Excel操作 ) は 静的リンク しかできないようだし...

かといって、MinGWでは そのままではできないようだし...

Q2.
また、DLLの関数に生成したオブジェクトを渡して間接的に使う方法で、なぜかdelete文が作動しません。

実行ファイル側でdeleteしてもクラッシュ(?)し、DLLの関数に任せる方法にしてDLL側でdeleteしてもクラッシュします。

(この実行ファイルで使っている)メモリは少ないのでしょうけど、COMを使っているので、
タスクマネージャーの"プロセス" に残ります。

( コマンドプロンプトのコマンド TASKKILL を使って、delete文の代わりに TASKKILLでプロセスを破棄する方法もありますが、
スマートじゃないので。 )

[環境等]
言語: C/C++ ( Windows API 実装あり )
環境(基本): MinGW (g++)
環境(DLL): VC++ 2010 Express
聞きたいこと1: なぜDLLのオブジェクトを実行ファイルで使おうとすると実行時エラーになるのか. また その回避方法.
聞きたいこと2: (動的リンク時)DLLのクラスオブジェクトをFactoryMethodパターン適用&生成し、DLLの関数にオブジェクトを渡し、間接的に操作する場合、delete文でクラッシュする. その回避方法.

宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

こんにちは。

まず、C++のクラス・オブジェクトをmsvcで生成し、MinGWで受け取れることにビックリなのですが、GetProcAddress()の時、関数名はどのようにして生成されたのでしょうか? マングルされているのでなかなか適切な名前を指定することはむつかしい筈です。
C++のシンボル名のマングル方式がmsvcとgccで全く異なるため、一般にはC++のクラスをお互いにやり取りできません。C言語I/Fの場合は事実上マングルされてないので大丈夫です。

次にmsvc側でnewしたインスタンスをMinGW側でdeleteするとクラッシュする問題ですが、ある意味それは仕様です。
①msvcはmsvcの標準ライブラリに含まれるメモリ管理ルーチンからメモリを取得します。
②MinGWはMinGWの標準ライブラリに含まれるメモリ管理ルーチンからメモリを取得します。
この2つのメモリ管理ルーチンの間に互換性はないため、①で獲得した領域を②の方式で解放すると矛盾が生じます。
その結果、クラッシュしているのだと思います。

異なるコンパイラを使う場合、DLLからexportするものをextern "C"することが一般的です。
そして、メモリ解放は獲得した側で行ういましょう。(同じメモリ管理ルーチンを使うこともできる筈ですが、やったことないので具体的な手順は知りません。)ファクトリー関数の逆のデストロイ関数を提供するイメージです。

投稿2016/06/30 05:35

編集2016/06/30 05:39
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2016/06/30 05:57

__declspec(dllexport) っていうものは DLLの関数定義時につけていますが、 extern "C" って、 extern "C"{ // ここにプロトタイプを定義 } でくくるやつでしょうか?
Chironian

2016/06/30 09:07

その通りです。
guest

0

なぜ実行ファイルで そのまま動かそうとするとクラッシュ状態になるのでしょうか。

実体は生成されているはずなのに...

これが何を言いたいのかがわかりません。デバッグでクラッシュしている箇所は見つけられないのでしょうか。
CreateInstance()やRunIE()などは自作メソッドなので、中身を提示していただかないとよくわかりません。

外部DLLを静的リンクせずに使用しているのであれば、WindowsAPIのLoadLibrary()やGetProcAddress()を使用されていると思いますが、そのあたりの結果はどうなっているのでしょうか?

Q2.

また、DLLの関数に生成したオブジェクトを渡して間接的に使う方法で、なぜかdelete文が作動しません。
実行ファイル側でdeleteしてもクラッシュ(?)し、DLLの関数に任せる方法にしてDLL側でdeleteしてもクラッシュします。

別プロセスでもなければ、確保したローカルメモリはどこででも解放できます。
実行ファイル(newした場所と同じ?)でdeleteしても失敗するということは、newで確保したアドレスと同じ物を渡していないか、型が違う(配列など)ものを渡しているのではないでしょうか。

投稿2016/06/30 04:46

shanxia

総合スコア1038

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

BeatStar

2016/06/30 04:59

CreateInstanceは 例外を受け取れば NULLを、それ以外なら インスタンスを返すようにしています。 LoadLibrary, GetProcAddressともに NULLかどうかをチェックしました。 結果 NULLではなかったので取得できているようです。 CreateInstanceで main関数内にインスタンス生成 -> インスタンスがNULLかどうかをチェックし NULLじゃなければ次へ -> GetProcAddressで取得したDLLの関数にオブジェクトを渡し、操作 -> main関数内でdeleteしようとしたら実行時エラー のような状態です。 main関数ではなく DLL側の DestroyInstance (定義済み) に オブジェクトを渡して DestroyInsntance 内でdelete しても 同じような結果です。
shanxia

2016/06/30 05:27

GetProcAddressで取得した関数の呼び出しは確実に成功しているのでしょうか? 次に、deleteはどの様に行っているのでしょうか? DLL側のDestoryInstanceとは、CInternetExplorerクラスのメソッドのことでしょうか? この場合は、「delete this;」などとして、デストラクタ内でFreeLibraryしていると言うことでしょうか? またクラッシュしたときに何かメッセージは出ていないのでしょうか? デバッグ版でビルドすれば、クラッシュ時に何か出るはずなのですが。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問