jl7gmnのblog

yahooブログから移行してきました。アマチュア無線を中心としたブログです。

2011年06月

vbausbio.dllをVisual C++2008 その4

イメージ 1

イメージ 2

イメージ 3

イメージ 4

USBIOのハード制御はひとまずアイデア待ちで小休止ですが、Visual C++2010Expressを用いたハムログへのデータ送出をやって見た。いままではVisualC++2008Expressでのプログラムコーディングが主としていたが、今回から方針を変えた。というのは、VC++2008Expressの入っているPCがとても遅くなってきたため、Vista に入れてあるVisualC++2010Expressを使うことにしました。画面もワイドなのでインターネットを検索しながらコーディングできる為好都合です。ということで今回からVisualC++2010Expressでのプログラミングということになります。もちろんVisualC++2008Expressも動作の確認、書き換え等の対処は行います。私の作業はWindowsXpがメインですから!。
VisualBasic 2008Expressではすでにハムログへのデータ送出関連はすべてできているから変数値の確認はVBを利用できるので安心だ。とにかくWindowsAPIなるものを使用して行うわけだが、やはりVisualC++2010ExpressのC++/CLIでの実際の利用例がないに等しい。もちろんマイクロソフトのホームページにはMSDNライブラリがある。これはやはりサンプルコードがあるが実際の例として使うには少しわかりずらい。何度本屋へ行って専門書を調べようかと思ったことか。今回悩んだのは対象Windowsフォームからハンドル取得のUser32ライブラリのFindWindows関数の引数の設定である。インターネットで調べたVC++2010での関数引数例をそのまま利用すると見事にすべてエラーが発生する。まず第一に宣言している内容がMSDNライブラリでは以下のようにかかれている.MFCの例だがAPIも踏襲されているので仕様はほぼ同じだ(似ている。?)
--------------------------------------------------------------------------------
static CWnd* PASCAL FindWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName
);
パラメーター
lpszClassName
ウィンドウのクラス名 (WNDCLASS 構造体) を示す NULL で終わる文字列へのポインター。 lpClassName が NULL のときは、すべてのクラス名が一致します。

lpszWindowName
ウィンドウ名 (ウィンドウのタイトル) を示す NULL で終わる文字列へのポインター。 lpWindowName が NULL のときは、すべてのウィンドウ名が一致します。
---------------------------------------------------------------------------------
このパラメータがインターネットの大概のサンプルでは第1パラメータと第2パラメータが逆になっているのだ。これは本当に困る。そのまま信じてコーディングするとエラーから抜け出せなくなる可能性が大である。MSDNライブラリできちんと第1パラメータはウィンドウのクラス名、第2パラメータはウィンドウ名(ウィンドウのタイトル)と記載されてる。今回はこのクラス名を調べるプログラムを利用してハムログのクラス名を確認して行った。VB2008Expressではハムログのクラス名は’TThwin'とJG1MOU浜田OMの資料HamlogMs.txt上に記載されているので、そのまま使用できる。これは作者でないとわからないわけだが、さきのクラス名を調べるプログラムは大変ありがたい。いろいろとあるようだが、私が使ったのはwinmap.exeというエクスプローラ風のアプリケーションです。これでハムログのクラスが間違いなく’TThwin'であることが確認できました。またパラメータの記載の方法もインターネット上の資料には正確に記載しているものが、ごくわずかでした。クラス名の第1パラメータへの記載の実際は
VC++2010Expressの例としてのNG例とOK例を以下に記します。実際にOK例はすべて確認済みです。
-------------------------------------------------------------------
[NG例]
Hwnd1=FindWindow(("TThwin"),NULL);
[OK例]
Hwnd1=::FindWindow((LPCTSTR)_T("TThwin"),NULL);
Hwnd1=FindWindow((LPCTSTR)_T("TThwin"),NULL);
Hwnd1=::FindWindow(_T("TThwin"),NULL);
-------------------------------------------------------------------
以上のとおりまず関数前の”::”がVisualC++2010Expressでは要るらしいが、なくてもコンパイルは通るようです。
またクラス名のパラメータ指定をマクロの"_T"で指定することである。(LPCTSTR)はあってもなくてもよいようです。ハムログのデータ送出で使う他関数についてはサンプルを(詳細は省略)以下に記します。また実際コードは画像を参考にしてください。
-------------------------------------------------------------------
『SendMessage関数』
::SendMessage(Hwnd2,WM_COPYDATA,wParam, lParam);
『SendMessage関数』
bool kite=0;
kite=::SetForegroundWindow(Hwnd2);
-------------------------------------------------------------------
ボタンをひとつ用意してボタンを押すとハムログのCALLのテキストにあらかじめ用意したデータのコールサインが送られるサンプルです。CALLを指定するデータはSendMessage関数のパラメータにlParamとして送られます。送出されると次の日付にフォーカスが移り終了します。これもSendMessage関数で任意の位置へカーソルを動かす指定をしてリターンコードを送るようにすれば自在にカーソルフォーカス位置の制御が可能です。VBでは自動でPCの日付を入れてコールサインへフォーカス指定の制御をしていました。
コマンドボタンでのハムログ入力ダイアログへのデータ送出がVC++/CLIで可能となりました。ついでにこの送出部を汎用性を持たせるべく関数化(戻り値なし)しました。
--------------------------------------------------------------------
『ヘッダー部での宣言』
void hamlogTX_datvc2010(int dcode,char hamdat[256]);
『プログラム上での関数の使用(ボタン内のコード)』
int dcode=1;
char hamdat[]="HAMLOG ";
hamlogTX_datvc2010(dcode,hamdat);

dcode=2;
char hamdat1[]="11/11/11";
strcpy_s(hamdat,hamdat1);
hamlogTX_datvc2010(dcode,hamdat);
--------------------------------------------------------------------
上はコールサインの場所にコマンド1を指定して"HAMLOG"データをセットしhamlogTX_datvc2010関数を指定します。同じルーチンで複数個所のデータを送る場合はデータはConstですので、別配列をそれぞれつくってそれにデータを入れそれを規定の配列にstrcpy_sでコピーしてhamlogTX_datvc2010関数を指定します。上はコールサインと日付が関数を利用してデータ送付される例です。
関数化すると本当に楽になります。関数の利用はdcodeにはハムログ入力ダイアログの送出場所指定番号、hamdatには送出文字データをセットして関数を指定します。
ここで注意することは、charの指定です。配列は[256]を予約していますが、最初のデータが入ったときにhamdatが自動にて入れたデータ文字数が予約されてしまいます。ですので、最初のデーターは文字データ数の最大を予想してスペースをデータとして入れてあります。そうしないと最初の文字データ+内部コード(改行等)の文字数よりデータ数が多いものを送るとエラーとなります。このブログ上では空白部が削除されてしまっています。"HAMLOG**************************************************";*はわかるように空白がわりに書き換えしました。実際は空白です。
またVISUALC++2008ExpressではUSER32.DLLの関数の宣言が異なりました。FindWindowの例を下記します。
---------------------------------------------------------------------
『VC++Express2010宣言』
[DllImport("user32.dll")]
extern System::IntPtr FindWindow(String^ className, String^ wndName);
『VC++Express2008宣言』
extern System::IntPtr __declspec(dllimport) __cdecl FindWindow(unsigned char className,unsigned char wndName);
---------------------------------------------------------------------
他インクルードヘッダーとして以下を使いましたので追加してます。
#include <tchar.h> // '_T'識別子用
#include <windows.h> // strcpy_s,strlen,他各定義用(HWND,LPARAM,........)
いつも思うのだが、このVisualC++2010Expressもそうだが、資料の公開があまりにもされていないので普及していないのだと思う、動くサンプルがユーザーを増やすのだがこれが少ないためユーザーが少ない。とにかく簡単なサンプルからはじめるのが一番良いと思っています。
 以上今回の作業は終了!。次はハムログからのデータ読み取りを行いたいですが、これまた大変そうであるので、まずは、UUSBIOのLEDの点滅の制御とか、現在のハムログの2重起動防止などの細かいところをやって見ようかしらん!?ステップバイステップ!! つづく

vbausbio.dllをVisual C++2008 その3

イメージ 1

イメージ 2

C++/CLIのプログラムはコントロールを使ってプログラミングしている限りVBやVCとあまり違いがないのがとても嬉しい。今回は以前作成したVBでのハムログ制御のVC++版をやってみようとおもっている。色々と制御以外のC++特有の処理とかが必要となるだろうけれどもまずはトライの精神で臨みます。プログラムも追加追加での対応なので、段々と拡張していき、フォームが大きくなっていきます。画面に収まっている時点では追加体制で、ある程度、機能がそろった時点で、新たに専用プログラム化することを一応目標としました。何かしらの目標がないと続かないと思って、まずは次のステップということで、アプリケーションの起動をプログラムで行います。もちろん対象のアプリケーションは、かの有名なハムログWINです。まずは買ってきてあるかんたんVC++のあらすじ読みてきなシェル系の目次をサーチし、次にインターネットでの検索サーチ、次から次とそれらしき資料が出てくるは、出てくるは、試してみるとエラーが出る。こんなことの繰り返しである。ところが、すばらしいネットの参考ブログかあった。今は伏せておくが、ヒントとして「IT.***」である。案外かゆいところへ手が届いていて私の性格にすんなりあった読みやすいし動作も間違いがないとてもすばらしいブログであると思う。とにかく検索すればすぐわかると思う。とにかく使えるサンプルがたくさんあるところが一番である。まずはさておいて、実際のアプリケーションを起動するプログラムコードは、Processを用いたものです。まずはそのセンテンスを以下に書きます。ハムログ起動のコマンドボタンのコードの中に書きます。
なお対象OSはWindowsXPを実使用予定でいますので、Visual C++2008Expressエディション(NetFramework3.5)が主となります。使用PC依存によります。Visual C++2010Expressエディション(NetFramework4.0)は使いません。ただし後で時間があれば試したいと思っています。(2010での他のNetFrameworkの選択は可能ですが...)
【ハムログ起動プログラム】
名前空間を記述します。(ヘッダー宣言部)
using namespace System::Diagnostics;

private: System::Void button11_Click(System::Object^ sender, System::EventArgs^ e) {
Process^ ps = gcnew Process();
ps->StartInfo->FileName = "Hamlogw.exe";
ps->StartInfo->WorkingDirectory = "C:\\Program Files\\HAMLOG2011-1";
ps->EnableRaisingEvents=true;
textBox6->Text="Hamlogw.exe ACTIVE!";
bool bSuccess;
bSuccess = ps->Start();
while (!ps->HasExited)
{
  Application::DoEvents();
}
textBox6->Text="Hamlogw.exe finish";
}

【起動プログラム説明】
psをマネージドタイプでインスタンス化します。
ProcessStartInfoクラスを使用してFileNameプロパティでアプリを指定します。
同様にWorkingDirectoryプロパティでハムログのあるパスを指定します。
ここで注意しなければならないのが、私も間違えてエラーを最初だしてしまいましたが、階層の記号「¥」を一つしか書かないとエラーが出ます。「\」は2つ必要です。
ProcessEnableRasingEventsをtrueに設定してプロセスが終了したときにイベントを発生させる設定をします。
ProcessStartでアプリケーションを起動します。
プロセスの起動状態はHasExitedプロパティで判断します。詳しい説明は先のブログにありますので確認してみてください。以上がプログラムでアプリケーションを起動するコードですが、起動したら、次はアプリを止める方法です。

【ハムログ強制終了プログラム】
private: System::Void button12_Click(System::Object^ sender, System::EventArgs^ e) {
//アプリケーションの強制終了
  array<Process^>^ ps = Process::GetProcessesByName("Hamlogw");
  for each (Process^ item in ps)
  {
   item->Kill();
  }
}

【強制終了プログラム説明】
これはこういうものだということでおまじない的にそのまま使います。
プロセスの名前を指定してそれをkillで強制終了させます。
以上で起動ボタンでハムログが立ち上がります。起動した画像も載せました。この状態でフォーム上からのUSBIOコントロールもハムログも問題なく動作しています。
ついでなのでフォーム上に時計をフォーム読み込み時に表示させました。私の作業時間が何時頃かがわかります。案外深夜から朝方が多いかも。今日のステップは終了します。次はなににしようかしらん!

vbausbio.dllをVisual C++2008 その2

イメージ 1

Visual C++2008Expressで作製したvbausbio.dllを用いたUSBIOインターフェースのコントロールプログラムでのタイマー機能とニューメリックコントロールを使ったプログラムの追加を行いました。プログラムの制御ではなくコントロールの使い方になります。私のVC++のWinndowsプログラムの言わば学習ステップです。
(1)タイマーコントロールをフォーム上に貼り付けます。
(2)ニューメリックコントロールをフォームに配置します。
(3)コマンドボタンを2つフォームに貼り付けます。(タイマーのスタートとストップ用)

【以下プログラムコード】
//グローバル変数の宣言を#programa onceの後に書きます。
int flagA;
int kankaku;


(省略)
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) {
//出力ポート1に0を出力
if (flagA == 1) {
  uio_out(1,0,0);
  flagA=0 ;
}
else{
  uio_out(1,15,0);
  flagA=1;
}
}
private: System::Void button9_Click(System::Object^ sender, System::EventArgs^ e) {
//int kankaku ;
flagA=0;
timer1->Interval=kankaku;

      timer1->Start();
}
private: System::Void button10_Click(System::Object^ sender, System::EventArgs^ e) {
timer1->Stop();
}
private: System::Void numericUpDown1_ValueChanged(System::Object^ sender, System::EventArgs^ e) {
//int kankaku;
kankaku=numericUpDown1->Value.ToInt32(numericUpDown1->Value) ;
}
【プログラム説明】
flagAはUSBIOへの出力をONとOFFをuio_outでそれぞれタイマー時間のインターバルで切り替えるためのフラグとしました。kakunin変数にはタイマーで指定するintervalへの時間値(msec単位)を入れます。これはニューメリックコントロールにて範囲1(≒1msec)から10000(10秒)までをプロパティのmaximum,minimunで設定します。コードはプロパティのイベント(雷マーク)を押してアクションのValueChangedをクリックしてコードをそこに書き込みます。ここで時間がかかりました。大変だったのは読み取ったValue値を変数のintに入れることでのエラー発生です。ToInt32を用いて32ビットのintに変換するのはわかったのですが、引数に何を入れるかが解りませんでした。実際の同じ値を示す部分を入れることで解決しました。(numericUpDown1->Value)しかしこの値を別のint変数に代入して変数化は出来ませんでした。もしかしたら変数でなくオブジェクトとして入れるべきなのか?いまははっきり解りません。
NG----------------------------------------------
int hensu;
hensu=numericUpDown1->Value
kankaku=numericUpDown1->Value.ToInt32(hensu) ;
------------------------------------------------
やはり直接でないとだめなのか?
c++の変数の使い方が間違っているのか?はっきり解らないのが実際のところです。まず簡便化はさておいて、動くセンテンスで進めました。
後はコマンドボタンをスタートとストップを用意してスタートではタイマーコントロールの開始制御を記述します。フラグ初期値を0に設定して、タイマーインターバルは先のニューメリックコントロールのkankaku変数を設定します。そしてタイマーのスタートをtimer1->Start();を書きます。ストップボタンは単にタイマーを止めるTimer->Stop();の一行です。
以上でビルドすると完成です。
実際の動作はニューメリックコントロールでタイマーインターバルを選択設定しスタートボタンを押すとタイマーインターバル値間隔でUSBIOの出力が点滅します。色々とインターバルを変えるとフラッシュの間隔が変わる為面白いですよ。
以上タイマーコントロールとニューメリックコントロールのC++での使い方が解りました。続く

vbausbio.dllをVisual C++2008,2010で使う

イメージ 1

イメージ 2

イメージ 3

イメージ 4

イメージ 5

イメージ 6

イメージ 7

イメージ 8

なにのきっかけだか、Km2NetのUSB-IOのインターフェースをC++で制御したくなった。と言うのも最近VisualC++2008ExpressでDLLライブラリを参照利用する方法について色々と調べていたのだ。ここにUSBIOというインターフェースがあったので、これでDLLの参照を試してみたわけです。以下はその内容について書いたものです。
今まではこのUSBIOをエクセルVBAとかVBnet、VB2005、VB2008で利用するDLLの使い方はパケさんの趣味の部屋で公開されており十分利用できていました。今回はVisualC++2008Expressと2010ExpressのC++での制御をトライしてみました。内容としてはライブラリとして公開されているvbausbio.dll(2007/05/18 12:40 制御関数は6つ以下参照)を読み込み制御するものです。ところでインターネット上ではこのUSBIOをVC++2008Expressで制御するフリーのサンプルは機能が制限されているものとして三石印房のUSB-IO専用 DLL Demoがあります。しかしながらこれはコンソール上でのサンプルで、目的としているCLRのWindowsフォームアプリケーションでありませんでした。検索しても何処にもVC++2008ExpressWindowsフォームでの使用例が無いものは何とかしてサンプルでも作るしかないと思い、色々と試してみたわけです。しかしVBの資料はたくさんあるがVC++2010,2008のDLL関数参照資料はほんとに少ないのには参った。
 まず最初に行ったのは、VBA、VB用として公開されているvbusbio.dllの関数をライブラリにEXPORTSすることです。これを行う方法はコンソール窓でコマンドライン処理(DOS式)にて行います。この方法はインターネットで"DLLファイルからlibファイルを作成"と検索すると色々と方法が出てきますので見て解りやすいものを応用します。
用意するものは 任意に作製したフォルダに dumpbin.exe ,lib.exe ,link.exe ,mspdb80.dll とライブラリを作るもとのdllの vbusbio.dll をコピーします。(最初のexeファイル3つはインストールされているのはMicrosoft Visual studio 9.0/vc/bin 最後のはMicrosoft Visual studio 9.0/Common7/IDEにあります。)実際にライブラリを作る手順は次の通りです。コマンドプロンプトで
dumpbin /exports vbausbio.dll > vbausbio.txt
で関数のリストが出てきます。これを関数のみに書き直しdefファイルの拡張子で保存します。vbausbio.defも画像添付しました。これを使いlibを作製します。次にコマンドプロンプトで 
lib /DEF:vbusbio.def /MACHINE:X86 /out:vbusbio.lib
と入力します。エラーがない場合に同フォルダにvbusbio.lib とvbusbio.expが出来ます。ここで出来たvbusbio.libを作製するプロジェクト上にvbusbio.dllと2つコピーします。ちなみにここでEXPORTSしたvbusbio.dllの関数は次の6つです。
[uio_find, uio-free, uio_getdevs, uio_inp, uio_out, uio_seldev]
以上のものをVisualC++2008ExpressのCLRのWindowsフォームアプリケーションで制御できるようにします。まずはプロジェクトを一つ作製します。フォームサンプルが作製されていてあとはツールボックからコマンドボタンとかコントロールを貼り付けたりします。実際のサンプルを載せました。Windowsフォームアプリケーションはウィンドーの処理とかは自動でコーディングされる為、案外複雑に見えますが、実際はコマンドボタン等のイベントを選びイベントの中にコーディングするのみで問題ありません。今回の一番のポイントはvbusbio.dllをc++で利用する時のヘッダーでの宣言方法です。これはヘッダーを新たに自分で追加しても良いし、最初から自動でできているstdafx.hのヘッダー内に追加してもいづれでも全く問題ありません。プログラム上で必要な位置で使用する前に宣言されていれば良いのです。ところで今回の関数の場合は以下のようになります。
extern "C" int __declspec(dllimport) __cdecl uio_out(int Port,int OutDat,int p3);
実際の例でC++Builderのサンプルがありますが、サンプルでは__stdcall が使われています。今回は上記のように宣言してOKとなります。(注意※C++Builder用のDLLやLIBやヘッダーは使いません)同様に残りの関数も宣言します。添付画像参照(省略)実際の制御関数を使ったプログラムは引数を間違えないように、宣言した通りに使用します。ここではC++Builder添付のヘッダーcppusbio.hの資料が大変参考になりました。コマンドボタンを押しコーディング部に ポート1にデータ0をパルス出力なしで書き換えるは uio_out(1,0,0); と書き込みます。以上でコーディングが完成したらビルドする前にプロジェクトにコピーしておいたvbusbio.libを登録します。プロジェクト->プロパティ->構成プロパティ->リンカ->入力->追加の依存ファイル右記入欄に vbusbio.lib と書き込みます。以上でVC++でライブラリがリンクされます。OKをおして戻ります。最終コーディングがすんだらビルドを行います。コーディングミスが一番多いです。問題がなければデバッグに実行ファイルが出来ています。後はスタンドアローンでライブラリを使わない設定にすれば実行ファイルが大きくなりますが単体で動く用にもできる様です。設定は割愛します。以上がVC++2008Expressでのvbausbio.dllをつかった制御でした。

Vista上のNETFramework4.0の最新のVisualC++2010Expressにても同プログラムを読み込ませ変換し動かしてみました。マネージデバッグアシスタントによりPinvoke関数 uio_outがスタックを不安定にしています。Pinvokeシグネチャがアンマネージターゲットシグネチャーに一致していないことが原因として考えられます。呼び出し規約、およびPInvokeシグネチャのパラメータがターゲットのアンマネージシグネチャに一致していることを確認してください。のエラーがプログラム起動後の関数uio-out実行時におきた。色々とインターネットでしらべたが、ライブラリ上の関数と呼び出しパラメータ宣言対応が違っている場合等に起きるようだ。ここではCallingConventionの例に従って宣言を以下のようにヘッダー部stdafx.hを書き換えます。

#pragma once 
using namespace System;
using namespace System::Runtime::InteropServices;
[DllImport("vbausbio.dll")]
extern "C" int uio_out(int Port,int OutDat,int p3);
以下省略
上記の宣言変更でPinvokeエラーは発生しなくなります。
VC++2008Expressで作製したプログラムはVC++2010Expressではエラーが起きる可能性がある事が解りました。とにかく実際に動かしてみるのが、問題発見が一番早いです。
また今回のVBAやVBNet等で利用するDLLは問題なく各VC++Express等で参照できることがわかりました。実際にDLLを作る場合の関数宣言を __stdcall と __cdeclで使い分けて参照の場合の確認等も行いましたが、やはり同じ宣言でないと _stdcallの場合は_stdcall,__cdeclの場合は__cdeclでの対応でなければエラーが出ました。重要なのは、オリジナルのヘッダー宣言部であるといえます。今回のvbausbio.dllの場合はC++Builderの参考資料があったためスムーズに行きました。ヘッダーの関数宣言資料がない場合はVB等の宣言から想定してヘッダーを起こすしかなかったと思います。このvbusbio.dllも進化しているようで現在では19関数まで増えているようです。またUSB-IO2.0もバージョンアップしてきて近日発売のようです。いづれにせよ目的のVC++2010,2008でのvbusbio.dllを使ったWindowsフォームサンプルが出来ました。あとは拡張あるのみ、最近はC++にのめりこんでいますが、C++の学習が追い付いていません。Windowsフォームのコントロールの使い方を習得するのも案外時間がかかるものです。特にExpress版でのMFCを利用できない場合のコントロールの動作サンプル作製はネットヘルプが主である為、特に時間がかかる。本も買ってはいるがMFC中心でコードは全く利用できない。まっ地道にやるしか仕方がないか。
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード
  • ライブドアブログ