PICでUSBを!(知識ゼロからのスタートです)
PIC18F14K50のUSB機能を100%自前のソフトで制御する試みです。しかもアセンブラで!
当記事は2009年12月から「TTLでCPUをつくろう!」というタイトルの もとにほとんど毎日連載をしてきたものを再編集したものです。 2011.7.10

前へ
次へ
目次へ戻る
ホームページトップへ戻る
☆C++でUSB(HID)アクセスプログラムを作成

PIC側のプログラムはC18コンパイラを使わずPICアセンブラで作成しますが、パソコン側のUSB(HID)アクセスプログラムはBorland C++コンパイラで作成します。
しかしこれがまた難物で悪戦苦闘の連続でありました。

[第43回]

●「外部シンボル …が未解決」

今回は、2ヶ月も前に書きました、[第40回]からの懸案でありました、「外部シンボル …が未解決」という意味不明のエラーがいよいよ、といいますか、やっと「解決」いたします。

わかっていらっしゃる方には、何てこともないようなエラーメッセージなのでしょうけれど、全然わかっていらっしゃらない私といたしましては、こういう意味不明のメッセージに直面すると、もうどこから手をつけていったらよいものか、途方に暮れてしまいます。

これが昔ですと、中区栄の丸善とか星野書店とかなどに足を運び(ああ。当地名古屋のお話です)、専門書のコーナーに入り浸りで片っ端から目次や索引をめくってまわる、ということになるのでありますが、今は有難い時代で、たいていのことはインターネットでけりがついてしまいます。

で、ネットで検索をして回りましたところ、なんとか見えてまいりました。
どうやら、この「未解決」というのは、そういう関数がみつからないよ、ということを意味しているらしいのですね。

うう。それなら、業界用語などをお使いにならないで、「みつからない」と直截におっしゃっていただきたいですう。
そんなわけのわからない業界用語をお使いになるものですから、パラメータがおかしいのか、とかまたWindowsのバージョンが違うのか、など悩まないでもよいところで悩んでしまって、何日も空費してしまうのでありますから…。

はなから、「みつからない」と言っていただければ、それはそれでいくらなんでもこれはおかしいぞ、ということで、それなりに調べ方もありますでしょうに。
だってincludeしておりますヘッダーファイルを調べましたら、ちゃんと定義してあるじゃありませんか。

Hidsdi.hの前半部分を以下に示します。

/*++
Copyright (c) 1996    Microsoft Corporation
Module Name:
    HIDDLL.H
Abstract:
    This module contains the PUBLIC definitions for the
    code that implements the HID dll.
Environment:
    Kernel & user mode
Revision History:
    Aug-96 : created by Kenneth Ray
--*/
#ifndef _HIDSDI_H
#define _HIDSDI_H
#include <pshpack4.h>
//#include "wtypes.h"
//#include <windef.h>
//#include <win32.h>
//#include <basetyps.h>
typedef LONG NTSTATUS;
#include "hidusage.h"
#include "hidpi.h"
typedef struct _HIDD_CONFIGURATION {
    PVOID    cookie;
    ULONG    size;
    ULONG    RingBufferSize;
} HIDD_CONFIGURATION, *PHIDD_CONFIGURATION;
typedef struct _HIDD_ATTRIBUTES {
    ULONG   Size; // = sizeof (struct _HIDD_ATTRIBUTES)
    //
    // Vendor ids of this hid device
    //
    USHORT  VendorID;
    USHORT  ProductID;
    USHORT  VersionNumber;
    //
    // Additional fields will be added to the end of this structure.
    //
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
BOOLEAN __stdcall
HidD_GetAttributes (
    IN  HANDLE              HidDeviceObject,
    OUT PHIDD_ATTRIBUTES    Attributes
    );
/*++
Routine Description:
    Fill in the given HIDD_ATTRIBUTES structure with the attributes of the
    given hid device.
--*/
void __stdcall
HidD_GetHidGuid (
   OUT   LPGUID   HidGuid
   );

ほうらね。
HidD_GetHidGuidもHidD_GetAttributesもちゃんとあるじゃありませんか。
そうすると、これでも「みつからない」というからには、これは相当におかしい何かがあるに違いない、ということになります。

ということで、さらに追求を続けた結果、やっとのことで正解にたどりつきました。

●C++とCの違いが関係しているらしい

どうやらこれは、”C++”と”C”の「名前の解決」の違いが関係している、のだそうです。

それならそうと言ってくださいよお。
ますますCの眷属全てが嫌いになってしまうじゃありませんかあ。

つまるところ、どういういきさつかはわかりませんが、このHidsdi.hなるものが、C++ではなくて、Cで記述されているらしいのでありますね(あるいは、Hidpi.hも、か?)。

なんでもCでの関数名とC++の関数名は、ソースプログラムでは同じに見えていても、コンパイルすると違うものになってしまうのだそうで…(うむむ。なんともややこしい)。

で、もともとCで書かれたソースプログラム(ヘッダーファイル)をC++で書かれたプログラムと一緒にC++でコンパイルすると、そのヘッダーファイルの先にあるはずの、ライブラリファイル(DLLの情報がある)との間で名前の整合性がなくなってしまって、そんな関数名はみつからないよ、というエラーメッセージになってしまう、ということのようであります。

じゃあ、その始末はいったいどうしてくれるのか、といいますと、そこで登場しますのが、extern ”C”です。

●extern ”C”

これもなんだかよくわからないものなのでありますが、これを”C”で書かれた関数名の前につけておきますと、そのところはC++コンパイラが、”C++”の名前ルールではなくて、”C”の名前ルールでコンパイルしてくれるのだそうです。

むむむ。よくはわからないのですけれど、やってみなけりゃあしょうがないのでありますから、とりあえず、Hidsdi.hの、

void __stdcall HidD_GetHidGuid (

のところを、

extern "C" void __stdcall HidD_GetHidGuid (

のように直して、コンパイルしてみました。
そうしましたら。



おお。
そのようにHidsdi.hを直す前は、
Error:外部シンボル…
というのが2つ表示されていたところが、直したあとでは、エラーが1つになりました。

画面の真中から下のあたりで実行しているコンパイルでは、
HidD_GetHidGuid( 
の前にextern ”C”を追加した、Hidsdi.hを作業中のフォルダに置いたあとでコンパイルしています。
HidD_GetHidGuid( については、エラーが出なくなりました。

これで、やっと「解決」です。
Hidsdi.hに定義されている全ての関数の前に
extern ”C”
をつければ、この問題は解決いたします。
やっとのことで、めでたし、めでたし、です。

しかし。
それはそれでよいのですけれど、全ての関数の前にextern ”C”をつける、というところが、
うむむ。
少しばかり面白くないなあ、という気がします。

で、もう少し調べてみましたら、こういう書き方もできることがわかりました。

(これより前の部分省略)
Revision History:
    Aug-96 : created by Kenneth Ray
--*/

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _HIDSDI_H
#define _HIDSDI_H

(この間省略)

#ifdef __cplusplus
}
#endif

Hidsdi.hのはじめの部分に、
#ifdef __cplusplus
extern "C" {
#endif
を書き加えて、終わりの部分に、
#ifdef __cplusplus
}
#endif
を書き加えます。

これこれ。
この方が、なんだかかっこいいように見えますねえ。

cplusplus ってなんじゃいなあ、と思ったのですけれど、
そうか、C plus plus(C++)のことだったのですね。

つまり、もしC++でコンパイルするならば、このソースプログラムの、その位置に
extern ”C” {
を置いて、そしてソースプログラムの最後のところにも、もしC++でコンパイルするならば、最後のその位置に

を置きますよ、ということです。

Hidsdi.hにそのようにして書き加えたものをHidsdi2.hとして作業中のフォルダに置き、そしてテストプログラムHidtest1_8_d.cppの、
#include "hidsdi.h"

#include "hidsdi2.h"
に直してから、コンパイルしてみました。

おお。エラーは出なくなりました。

うう。
しかし。

最初は、ちょいとかっこいい、と思った、
#ifdef __cplusplus
ですけれど、よくよく考えてみると、それもなんだか回りくどいのではないですかあ。

「もしも」、も何も、私ははじめからC++を使うつもりなので、そのほかの言語を使うつもりはさらさらありませんから、この #ifdef __cplusplus は全く余計な気がします。

それならば、いっそ、こんな具合にできませんかねえ。

Hidsdi.hはもとの最初のままのものを使うことにしまして、HIDテストプログラムの、

#include "hidsdi.h"

のところを、

extern "C" {
#include "hidsdi.h"
}

にしてみました。

HIDテストプログラムのソースリストは[第39回]にあります(そのリストでは、#include "hidsdi2.h" になっています)。

これをHidtest1_8_e.cppという名前にしたうえで、コンパイルしてみました。


おお。どうやらエラーにならずにうまく通ったようです。
これなら、文句無し、です。
めでたし、めでたし、でした。
CPUをつくろう!第470回(2010.4.5upload)を再編集

PICでUSBを![第43回]
2011.7.10upload

前へ
次へ
目次へ戻る
ホームページトップへ戻る