DLLのクラスオブジェクト操作ができない...
解決済
回答 2
投稿
- 評価
- クリップ 0
- VIEW 3,036
C/C++でやっています。
基本的には MinGW (g++) でやっているのですが、(C/C++側で)IE操作やExcel操作もやってみたいと思い、
VC++でIE,Excel用のそれぞれのDLLを作成しています。
質問は2つほどあります。
Q1.
IE操作用 インターフェースクラス ( たとえば IInternetExplorer ) を定義し、
それを継承して 実際に動くクラス ( たとえば CInternetExplorer ) を定義し、
FactoryMethodパターンを適用して CreateInstance関数 でオブジェクトを生成しています。
// VC++ でのDLL作成
// ここでタイプライブラリの取得(?)
// ...
// インターフェースクラス
class IInternetExplorer
{
public:
virtual ~IInternetExplorer(){}
virtual bool Run( bool bVisible ) = 0; // IE起動
virtual bool Open( char *url ) = 0; //
...
};
// 実際に動くクラス
class CInternetExplorer : public IInternetExplorer
{
public:
CInternetExplorer();
~CInternetExplorer();
bool Run( bool bVisible ); // IE起動
bool Open( char *url ); // url のところに...
...
};
IInternetExplorer* CreateInstance( bool bVisible );
(一応、メンバ関数定義ありますが、省略...)
それで、
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文でクラッシュする. その回避方法.
宜しくお願い致します。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
こんにちは。
まず、C++のクラス・オブジェクトをmsvcで生成し、MinGWで受け取れることにビックリなのですが、GetProcAddress()の時、関数名はどのようにして生成されたのでしょうか? マングルされているのでなかなか適切な名前を指定することはむつかしい筈です。
C++のシンボル名のマングル方式がmsvcとgccで全く異なるため、一般にはC++のクラスをお互いにやり取りできません。C言語I/Fの場合は事実上マングルされてないので大丈夫です。
次にmsvc側でnewしたインスタンスをMinGW側でdeleteするとクラッシュする問題ですが、ある意味それは仕様です。
①msvcはmsvcの標準ライブラリに含まれるメモリ管理ルーチンからメモリを取得します。
②MinGWはMinGWの標準ライブラリに含まれるメモリ管理ルーチンからメモリを取得します。
この2つのメモリ管理ルーチンの間に互換性はないため、①で獲得した領域を②の方式で解放すると矛盾が生じます。
その結果、クラッシュしているのだと思います。
異なるコンパイラを使う場合、DLLからexportするものをextern "C"することが一般的です。
そして、メモリ解放は獲得した側で行ういましょう。(同じメモリ管理ルーチンを使うこともできる筈ですが、やったことないので具体的な手順は知りません。)ファクトリー関数の逆のデストロイ関数を提供するイメージです。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
なぜ実行ファイルで そのまま動かそうとするとクラッシュ状態になるのでしょうか。
実体は生成されているはずなのに...
これが何を言いたいのかがわかりません。デバッグでクラッシュしている箇所は見つけられないのでしょうか。
CreateInstance()やRunIE()などは自作メソッドなので、中身を提示していただかないとよくわかりません。
外部DLLを静的リンクせずに使用しているのであれば、WindowsAPIのLoadLibrary()やGetProcAddress()を使用されていると思いますが、そのあたりの結果はどうなっているのでしょうか?
Q2.
また、DLLの関数に生成したオブジェクトを渡して間接的に使う方法で、なぜかdelete文が作動しません。
実行ファイル側でdeleteしてもクラッシュ(?)し、DLLの関数に任せる方法にしてDLL側でdeleteしてもクラッシュします。
別プロセスでもなければ、確保したローカルメモリはどこででも解放できます。
実行ファイル(newした場所と同じ?)でdeleteしても失敗するということは、newで確保したアドレスと同じ物を渡していないか、型が違う(配列など)ものを渡しているのではないでしょうか。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.21%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2016/06/30 14:57
extern "C" って、
extern "C"{
// ここにプロトタイプを定義
}
でくくるやつでしょうか?
2016/06/30 18:07