DOBON.NET DOBON.NETプログラミング掲示板過去ログ

C#のCOMコンポーネント参照設定について

環境/言語:[WindowsXP, C#, .NET Framework 3.5]
分類:[.NET]

皆さんこんにちは。
現在大学の研究でUSBボードを使ってモーターを動作させるプログラムを
書いています。
開発環境はWindowsXp Professional sp3で、
使用言語はC#, Visual Stadio 2008で開発を行っています。
すでにボードはありまして、kumagai電機のKp422snuAというボードを使って
パルスを発生させようとしています。
http://www.kumagai.co.jp/index.html

そこで、まずはPCがボードとつながっているか、簡単にC#で参照設定したところ、
COMコンポーネントであるらしく、オブジェクトブラウザで見ることができました。
ですが、そのオブジェクトをnew演算子で作成し、操作しようとすると、
「'System.Runtime.InteropServices.COMException' 
のハンドルされていない例外が mscorlib.dll で発生しました。
追加情報: 致命的なエラーです。 
(HRESULT からの例外: 0x8000FFFF (E_UNEXPECTED))」
とのエラーが発生し、オブジェクトが想定どおりに作られていない模様です。
もしCOMコンポーネントを利用して動作させる方法をご存知の方、
何かアドバイスを御教示いただければ幸いです。

実際書いたプログラムは以下のとおりです。

using System;
using System.Drawing;
using System.Windows.Forms;

class AppMain : Form
{
    KPSLib.KpsClass kps;
    public static int Main()
    {
        Console.WriteLine( "Hello! This is C# world!" );
        AppMain cApp = new AppMain();
        Application.Run( cApp );
        return 0;
    }

    public AppMain()
    {
      int err_code;
      kps = new KPSLib.KpsClass();
      err_code = kps.OpenKps( 0 );// <-- ここでエラーが発生します。
      Console.WriteLine( "err : {0}", err_code );
      kps.Close();
      Console.ReadLine();
    }
}
■No24538に返信(rkさんの記事)
> 皆さんこんにちは。
> 現在大学の研究でUSBボードを使ってモーターを動作させるプログラムを
> 書いています。
> 開発環境はWindowsXp Professional sp3で、
> 使用言語はC#, Visual Stadio 2008で開発を行っています。
> すでにボードはありまして、kumagai電機のKp422snuAというボードを使って
> パルスを発生させようとしています。
> http://www.kumagai.co.jp/index.html
>
> そこで、まずはPCがボードとつながっているか、簡単にC#で参照設定したところ、

  当然、ボードが無いと何ともできませんが・・・

  ワークショップをインストールしてボードのコンフィグレーションを
  行い、そのコンフィグレーション番号をOpenKps()に渡して行うよう
  に思いますが・・・

  コンフィグレーションしましたか?

  後、ActiveX OCXなので・・・もしかしたら、OCXをフォームに貼って
  やった後、ラッパー設定が為されるので、そのラッパー名である
  『AxKPSLib.AxKps』
  で、行うとうまくいくかも・・・

  イベントもありますので、AddHandler でイベント受信関数を登録する
  か、

  Private WithEvents devKps As AxKPSLib.AxKps

  とするか・・・

  何にせよ、ちょっと厄介なActiveXかと思います。

※ C言語用の開発キットもあるようなので、C++ CLIで呼び出しモジュール
  を作って、C#から呼び出す・・・と云う方法もあろうかと。

  いかんせんボードが無いので実験不可能・・・

  頑張って下さい!

以上。
オショウさん
早速のご返信ありがとうございます。

>   当然、ボードが無いと何ともできませんが・・・
>   ワークショップをインストールしてボードのコンフィグレーションを
>   行い、そのコンフィグレーション番号をOpenKps()に渡して行うよう
>   に思いますが・・・
>   コンフィグレーションしましたか?

Setupで使われているボード番号は一枚しかありませんので、
0番が登録されている状態です。ワークショップはまだ試していませんので、
調べてから実行してみます。

>   後、ActiveX OCXなので・・・もしかしたら、OCXをフォームに貼って
>   やった後、ラッパー設定が為されるので、そのラッパー名である
>   『AxKPSLib.AxKps』
>   で、行うとうまくいくかも・・・

私もちょっとそのように考えて、フォームに貼り付けて見たのですが、
下記のようなエラーが出てしまい、現在この動作の原因を調査中です。

追加情報: 現在のスレッドはシングル スレッド アパートメントでないため、ActiveX コントロール 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx' をインスタンス化できません。

> ※ C言語用の開発キットもあるようなので、C++ CLIで呼び出しモジュール
>   を作って、C#から呼び出す・・・と云う方法もあろうかと。

いろいろな情報ありがとうございます。
実はこのC#の前にVC++で何とかならないか?と思い、
VC++でこのOCXを使おうと思ったのですが、VBのように簡単に
コンポーネントを使うことが、今の私ではできず、C#なら
もしかしたら簡単なのかもしれないと思い立ちいろいろ調べてみている
ところなのです。それならVBでいいじゃん!と思いそうですが、
C#が非常に面白いので、できればこういった難解なコンポーネントも
使えるようになりたいのです。

>   いかんせんボードが無いので実験不可能・・・

とりあえず、ボードが無いPCでもボードを探しに行った時に、
ボードなし(1011)のエラーが帰ってくれば御の字だと思っていたのです...
それが、OCXへのアクセスに苦労するとは...といった状況です。

本当にありがとうございました。まだまだ調べてみます。
■No24540に返信(rkさんの記事)
> 追加情報: 現在のスレッドはシングル スレッド アパートメントでないため、ActiveX コントロール 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx' をインスタンス化できません。


public static int Main()
{
}

の部分を

[STAThread]
public static int Main()
{
}

にしてみるとどうでしょうか?
VB6ならエラー無く、コード(1009)が返ってきました。

このActiveX OCX なかなか問題があるように思います。
.NETから使うのは難があるかと・・・

※ 一応、メーカーに問い合わせてみたら?

以上。
流れを見ていて気になりましたのでお尋ねしたいと思います。

■No24542に返信(オショウさんの記事)
> このActiveX OCX なかなか問題があるように思います。
> .NETから使うのは難があるかと・・・
「問題がある」、「使うのは難がある」とされる根拠は何かあるのでしょうか?

根拠が明確にあり、それを解説した上でのアドバイスであれば有益だと思いますが、根拠が示されない状態で不安を煽る内容は頂けません。



MainメソッドにSTAThread属性がついていないだけという可能性も残る現状では、結論を急ぐには早すぎるかと思っています。
■No24544に返信(Azuleanさんの記事)
> 流れを見ていて気になりましたのでお尋ねしたいと思います。
>
> ■No24542に返信(オショウさんの記事)
>>このActiveX OCX なかなか問題があるように思います。
>>.NETから使うのは難があるかと・・・
> 「問題がある」、「使うのは難がある」とされる根拠は何かあるのでしょうか?
>
> 根拠が明確にあり、それを解説した上でのアドバイスであれば有益だと思いますが、根拠が示されない状態で不安を煽る内容は頂けません。
>
> MainメソッドにSTAThread属性がついていないだけという可能性も残る現状では、結論を急ぐには早すぎるかと思っています。

  テストしましたが、残念ながら効果なしです。

  ActiveX OCX は私も作ってましたが、いい加減な作りだとVB6だけ
  の環境でしか動作しないものになってしまいますし・・・

  今回のものがそれに当てはまるか・・・と云われれば、VB6でフォ
  ーム貼ろうが、CreateObjectしようが異常終了しなくてエラーコード
  gは返ります。

  .NETでラッパーさせてフォームに貼っても、異常終了すると云うので
  あれば、正常な作りではない可能性が高いと思います。

※ ひとつ実験していませんが・・・
  Form_LoadでActiveXが初期化されないと云うような問題があったよう
  に思います。WndProcをオーバーライドして、ActiveXの初期化を強制
  的に行えば可能性はあるかも・・・

  確かそのようなことがあったと記憶しています。

  何かのヒントになれば・・・

以上。
.NETでActiveX OCXをフォームに貼った場合、Form_Loadが発生しない
問題は、この件には無関係でした。

試してみましたが、Form_Load発生していましたし・・・

さて何が根本原因か・・・
ボードが無い状態でこれ以上調べるのは、ちと困難・・・

以上。あしからず。
私も追試しましたが、フォームに貼ること、OpenKpsをとりあえず呼んでみることはできました。
(参考:MainスレッドにSTAThread属性をつけていないと実行時にエラーになります)

それ以上はボードを持っていないと試せないことであり、また、E_UNEXPECTEDという例外コードは外部から原因を推定することは困難です。

うまくボードを持っている人が居合わせないと回答をもらえないかも。
オショウさん
魔界の仮面弁士さん
Azuleanさん

有用な情報ありがとうございました。
昨日の夜自宅に帰ってから、エラーの原因となる部分について、
調べていたのですが、[STAThread]属性をつけることによって
実行時のエラーは回避されました。ありがとうございました。
いまいちSTAの概念が分かっていないのですが、
もともとSTAで動くCOMで提供されているので、STAなんだからCOMクライアントも
そのように動きなさいという認識でよろしいでしょうか?
大体において待ち時間の多いモーターを動かすボードドライバなので
マルチスレッドを要求するようなものは書くつもりが無いのですが、
ここでひとつ疑問があります。

Main関数の前に[STAThread]属性をつけた場合、そのクラス全体がシングル
スレッドで動くということなんでしょうか?
たとえばモーターへキャンセルを伝えたい場合、モーターを動かす
スレッドとキャンセル画面を受け付けるスレッドに分けたい場合、
[STAThread]属性は邪魔になったりしないのでしょうか?

なお、プロパティで提供されているものを使おうとすると
エラーで落ちてしまうものもあり、ちょっと意味不明な動作も
してしまいます。VB6.0なら動くのに...(.NETでは未検証)
過去の遺産を使おうとするスケベ心がいけないのか、
まだまだ勉強不足なだけか...
COMのバイナリ互換を信用しすぎているだけなのか...

ともあれ、動かせそうなめどが立ちました。
情報を提供してくださった皆さん。
わざわざドライバのダウンロードまでして試してくれた皆さん。
本当にありがとうございました。
解決済み!
Atata!!です。


> もともとSTAで動くCOMで提供されているので、STAなんだからCOMクライアントも
> そのように動きなさいという認識でよろしいでしょうか?

この認識で正しいです。ActiveXコントロール(OCX)はSTAでしか動かないので、
クライアントもそれに合わせなければなりません。


> Main関数の前に[STAThread]属性をつけた場合、そのクラス全体がシングル
> スレッドで動くということなんでしょうか?

ご自分で確かめるのが良いかと。


> たとえばモーターへキャンセルを伝えたい場合、モーターを動かす
> スレッドとキャンセル画面を受け付けるスレッドに分けたい場合、
> [STAThread]属性は邪魔になったりしないのでしょうか?

「モーターへキャンセルを伝えること」を「ActiveXコントロールを呼び出すこと」と
言い換えることができるならば、(あえて簡単な文章では説明しませんが)
RCWはスレッド間の同期を自動的に行うため同期に関する問題は発生しません。

[STAThread]属性を付けていなかった時に発生していた問題が発生するとは考え難いということです。多分。


> なお、プロパティで提供されているものを使おうとすると
> エラーで落ちてしまうものもあり、ちょっと意味不明な動作も
> してしまいます。VB6.0なら動くのに...(.NETでは未検証)

ActiveXコントロールのタイプライブラリをざっと見てみました。
残念ながらインデクサ以外のインデックス付きプロパティはC#ではサポートされていません。
さらにVB.NETではタイプライブラリインポータ(TLBIMP.EXE)の問題で使えなかった記憶があります。(Visual Studio 2005以降は未確認)
C#では確実にこのコントロールのすべての機能を使用することはできません。
VB.NETでは、自分で相互運用アセンブリを記述し、それをActiveXインポータ(AXIMP.EXE)で指定すれば可能なはずです。
> VB.NETでは、自分で相互運用アセンブリを記述し、それをActiveXインポータ(AXIMP.EXE)で指定すれば可能なはずです。

考えてみたらVB.NETで相互運用アセンブリは記述できません。
相互運用アセンブリはILかC#で記述しなければなりませんが、
C#はインデックス付プロパティが無理なので、IL以外の選択肢は無いです。

と言うか、IL使うぐらいならC++/CLIの出番かと。
Atata!!さん

ご指摘ありがとうございます。
実は昨日の動作確認で、[STAThread]の後、新たにスレッドを作ったら
どうなるか、調べてみました。

結果:スレッドは作られるけど、Whileが回らない??

というちょっとよく分からない状況になりました。
分岐スレッドの最初でConsole.Writeで表示を行わせているのですが、
While文の中であるものが回らない...かといって終了もしないという
??な状態でとまる現象です。スレッド作られないとかならまだ
分かるのですが...

ここまで書いて、ふと思ったのですが、Mainで[STAThread]を使うから
よくないのであって、Mainの中に2つのスレッドを作って、それぞれのスレッド
に対し、[STAThread]を指定すれば、モーター状況を常に監視するスレッドと
分けられるのではないかと思うのですが...
そもそもそんな設定できるのか、アプリケーションに1スレッドだから
STAなのか...ちょっと分かっていませんが、何より、

...これってSTAか?(笑)

ActiveXにこれほど悩ませられるとは...思っても見ませんでした。
最悪の手法として、VB6.0でDLLを作成し、C#で使うことを検討します。
モーターが動いたのでやった!と思ったのですが、C#からすべて使えないと
考えると、悲しくなりますね...。
後はそれよりモーターをスムーズに動かす数値を見つけなければ...
でもこれは別のお話(笑)

有用な情報ありがとうございました。
2009/05/14(Thu) 10:36:10 編集(投稿者)

■No24557に返信(rkさんの記事)
> 実は昨日の動作確認で、[STAThread]の後、新たにスレッドを作ったら
> どうなるか、調べてみました。

KPSLib.Kps とやらを見た事が無いので、具体的な回答はできないのですが、
VB6 向けActiveX コントロールの場合、単に new するだけでは動作せず、
フォームに貼り付けて利用することが期待されている物も多いので
注意してください。(たとえば、メッセージループを必要とする物など)

なので、コントロールそのものは UI スレッドで用いた方が良いかと思います。


> VB6.0でDLLを作成し、C#で使うことを検討します。
コンテナのエクステンダ オブジェクトあるいはアンビエント プロパティへの
依存性の都合などから、VB6 のフォーム以外での動作をサポートしない OCX も
稀にあります。Kps がそうであるかどうかは分かりませんけれども。
■No24553に返信(Atata!!さんの記事)
> 残念ながらインデクサ以外のインデックス付きプロパティはC#ではサポートされていません。
自動生成された IA では、代替のget/setメソッドが実装されたような気が。

> さらにVB.NETではタイプライブラリインポータ(TLBIMP.EXE)の問題で使えなかった記憶があります。(Visual Studio 2005以降は未確認)
TLBIMP.EXE での動作は確認していませんが、VS の参照設定においては、
.NET のバージョンによって、実装が変化する事はあるかも知れません。

.NET 1.1 の頃、MSHFlexGrid を VB から利用した時に、SP の有無によって
 AxMSHFlexGrid1.set_Cols(10)   'SP 無しだとエラー、SP1 だと動作
 AxMSHFlexGrid1.set_Cols(0, 10) 'SP 無しでも SP1 でも動作
という違いが生じる事がありましたので。
魔界の仮面弁士さん

貴重な情報ありがとうございます。

> フォームに貼り付けて利用することが期待されている物も多いので

フォームに貼り付けなければいけないのならば、
DLLでフォーム作って、そこに貼り付けてフォームごと
非表示って方向で考えています。というか、C#でも実際
フォームに貼り付けちゃっているんですけども(^^;;

そんなわけで僕的には最悪なDLLの作り方ですね(笑)
ちなみに、C#でテスト的に動かしているときには、
Formを継承したクラスの中で、Application.Runを
せず、コンストラクタの中でループまわして全部処理しています。
これだとフォームがとりあえず見えて無くても、
メッセージループを受け取れなくても動いているように見えるので、
一応KPSはもしかしたらnewだけでも動くように思えてきました。
しかし、貼り付けないとobjectがなくなってしまうので、どうやって
使うのかが分からないので、とりあえずは現状で動かします。
最終的にはGUIにするので、フォームに貼り付けちゃっても
問題ないのですが、なんだか気持ち悪いですね(笑)

>> 残念ながらインデクサ以外のインデックス付きプロパティはC#ではサポートされていません。
> 自動生成された IA では、代替のget/setメソッドが実装されたような気が。

これについては、自動作成された代替のget/setメソッドで動いているようです。
想定した出力ポート(4軸)へ出力できてモーター君も元気に回っています。

本当にいろいろ教えていただき、誠にありがとうございました。
フォローありがとうございます。

>自動生成された IA では、代替のget/setメソッドが実装されたような気が。

5年程前(VS2003)、代替メソッドが正常に動作しなかった記憶があったのですが、今試す(VS2005)と正常に動作しました。
当時発生していたと記憶している事象は代替のget/setメソッドを使用した際、IDispatch::Invokeの呼び出しでDISPATCH_PROPERTYGET/DISPATCH_PROPERTYPUTではなく、
DISPATCH_METHODが指定されていたというものです。
このレベルの動作が変更されるとは思えないので、私の勘違いのような気がしていますが・・・。

やはり何事も検証してみることが大事ですね。

DOBON.NET | プログラミング道 | プログラミング掲示板