VB6/VBA → VB.NET 移植メモ

他にもたくさんあるだろうが、気づいたものを列挙。

大きな違い

  • いちばんの違いは、本格的なクラスベースのオブジェクト指向言語になったこと
    • 既存のVB6/VBAのソースがきちんとクラス化/モジュール化されていない密結合なコードの場合、ソースをなるべくそのまま流用するのであれば下記のような方法を取る。
    • (1) モジュールで実装 (静的クラスのようなもの)
    • (2) 静的メソッドで実装 (Public Shared Sub/Function)
    • (3) 単一のクラスで実装し、Partial Class でクラスを分割
    • もちろん、スケジュールとコストが許すなら再設計が望ましい。
  • 次に大きな違いは、スレッドが使えるようになったこと
    • VB6/VBAはシングルスレッドなので、時間のかかる処理も DoEvents でごまかしていた。VB.NETではスレッドで処理すべき。

基本的な文法の差異

  • Attribute VB_Name → クラス名/モジュール名
  • Option Explicit はデフォルトで On (明示的な変数宣言が必要)
  • Option Strict が追加 (厳密な型指定を強制。デフォルトはOff)
  • Type → Structure に置換 (メンバの Dim → Public に置換)
  • Variant → Object に置換
  • Wend → End While に置換
  • If文の終わりにEnd If が必須 (VB6/VBAでは1行の If 文には不要)
  • Continue For/While/Do, Exit For/While/Do が追加
    • GoTo文からの書き換えが望ましいが、多重ループ脱出は要注意
  • Try Catch文が追加 (構造化例外機構)
    • On Error GoTo文は書き換えが望ましい
    • Continue, Exit, Catch, Finallyなどは予約語となったので、ラベル名に使っていた場合は注意して書き換えが必要
  • VB6ではIntegerが16bit, longが32bit → VB.NETではShortが16bit, Integerが32bit, longが64bit
  • Sub/Functionの引数は ( ) で囲む必要がある
  • VB6では引数はByRefがデフォルト → VB.NETではByValがデフォルト
  • 代入文の Set と Let → 廃止 (削除する)
  • 動的配列の ReDim の末尾に「As 型名」を付けない
  • 多次元の動的配列の定義は次元を明示
    • Dim hoge() As String → Dim hoge(,) As String
  • 構造体のメンバーである配列は初期サイズを指定できない → コンストラクタで RiDim する
  • Option Base 1 (配列を1始まりに設定) → 廃止 (用いられている場合は慎重な修正が必要)

GUI関連

  • MsgBox("メッセージ") → MessageBox.Show("メッセージ")
  • フォーム名.Show(vbModal) → フォーム名.ShowDialog()
  • フォーム名.Repaint → フォーム名.Invalidate()
  • Unload フォーム名 → フォーム名.Close()
  • コントロール名_イベント名 (暗黙のイベントハンドラ名)
    → フォームデザイナでイベントとハンドラをひもづけ
  • コントロール配列 (ButtonArray等)は廃止
    → コードでコントロールの配列(Button[ ]等)に格納
    • Load buttonHoge(i) → buttonHoge(i) = New Button()
    • ハンドラもコードで設定。AddHandler buttonHoge(i).Click, AddressOf buttonHoge_Click
    • ハンドラでは sender で判定。Index → Array.Index(buttonHoge, (button)sender)
  • Form_Initialize → Form.Loadイベント
  • Form_QueryClose → Form.FormClosingイベント
    • Cancel → e.Cancel, CloseMode → e.CloseReason
  • Form_Unload → Form.FormClosed イベント
  • CommandButton → Button
  • Label.Caption → Label.Text
  • ProgressBar.Min/Max → ProgressBar.Minimum/Maximum
  • 寸法の単位 Twip → ピクセル (96dpiの環境下では15Twipで1ピクセル)
  • PictureBox → 互換性が無い (画面描画の仕組みが一新された)
  • PopupMenu → ContextMenuStrip
    • contextMenuStripHoge.Items.Add(toolStripMenuItemPiyo)
    • Me.ContextMenuStrip = contextMenuStripHoge
  • FileListBox → 廃止 (いちおう互換クラスはあるが)
  • DoEvents → いちおうApplication.DoEvents() はあるが使わない方が良い
    • 上記のようにスレッドを用いた処理に変更すべき

Excelの操作 (VBAからの移植の場合)

  • Microsoft Excel xxx Object Library を使用する
    • [プロジェクト]→[参照の追加]→[COM] で Microsoft Excel xxx Object Library にチェック
    • ソースの冒頭に Imports を追加
Imports Microsoft.Office.Interop
Imports Microsoft.Office.Interop.Excel ' お好みでこちらも

ファイル操作

  • ファイル操作系も一新されたので作り直したほうが良い。(いちおう互換クラスはある)
  • ファイルの存在確認: Dir(パス) <> "" → File.Exists(パス)
  • ファイルを開く/保存のダイアログ CommonDialog → OpenFileDialog / SaveFileDialog
  • VBA の FileSystemObject → System.IO.Path / File / Directory
    • FSO.BuildPath(,) → Path.Combine(,)
    • FSO.GetFileName() → Path.GetFileName()
    • FSO.DeleteFile() → File.Delete()
    • FSO.GetFolder().SubFolders → Directory.GetDirectories()
    • FSO.GetFolder().Files → Directory.GetFiles()

その他

  • LenB("文字列") → Encoding.GetEncoding(Encoding.UTF8).GetByteCount("文字列")
  • String(文字数, "A") → New String("A"c, 文字数)
  • Format(hoge, "書式") → hoge.ToString("書式") または String.Format("書式", hoge)
    • 書式の記法の違いは要修正
  • Date など予約語が増えたので、変数名などに使っている場合は要修正

VB6→VB.NETの自動変換

下記の記事を参照。

いろいろなメモ(3)

Visual Basic 6.0 でOCXが読み込めないとき

VB6のプロジェクトを開くときに「MSCOMCTL.OCXを読み込めません」というエラーが出る場合、
メニューの 「プロジェクト」→「コンポーネント」→「参照」ボタンで、

Microsoft Windows Common Controls 6.0(SP6)
(C:\Windows\System32\MSCOMCTL.OCX)

をチェック。


C#で「あいまいな参照」エラー

using 識別子 = 全名; で解決。

エラー CS0104 'Application' は、'Microsoft.Office.Interop.Excel.Application' と 'System.Windows.Forms.Application' 間のあいまいな参照です。

using Application = Microsoft.Office.Interop.Excel.Application;

いろいろなメモ(2)

IARでスタック解析

  • 「プロジェクト」>「オプション」>「リンカ」>「アドバンスト」>「スタック使用量解析を有効化」をチェック
  • 割り込みハンドラやOSタスクの関数は制御ファイル(.suc)で指定しておく
call graph root [task] : test_task;
call graph root [interrupt] : SysTick_Handler, UsageFault_Handler;
  • mapファイルの 「STACK USAGE」セクションにスタック使用情報が出力される

【参考記事】
スタック使用とスタック使用解析制御ファイル | IAR Systems

VB6ランタイムのインストール

Windowsで古いVB6のアプリを実行しようとすると、「comdlg32.ocx がない」というようなエラーが出る。

VB6ランタイムを下記からダウンロードしてインストールする。(公式はすでにサポートしていない模様)

VirtualBoxのあれこれ

(1) 画面がリサイズされない場合

仮想マシンの「設定 > ディスプレイ > スクリーン > Graphic Controller」を「VMSVGA」から「VBoxVGA」に変更。

(2) Virtual Boxにマウスとキーボード操作を取られた場合

右Ctrlキーを押す。 (デフォルトのホストキー)

  • 右Ctrlキーがない場合は、Ctrl + Alt + Del
  • ホストキーの設定は、「ファイル > 入力 > 仮想マシン > ホストキーの組み合わせ」
(3) 仮想ハードディスクファイルから仮想マシンを作成

仮想マシン > 新規」で「すでにある仮想ハードディスクファイルを使用する」を選択し、*.vdiなどの仮想ハードディスクファイルを指定。

(4) ハードディスクの追加

仮想マシンの「設定 > ストレージ > コントローラー:SATA」で「ハードディスク」を追加。(*.vdiなどの仮想ハードディスクファイルを指定。)

(5) 光学ドライブにISOイメージをマウント

仮想マシンの「設定 > ストレージ」で「空」となっているCD/DVDドライブを選択し、一番右にあるディスクアイコンをクリックし、「光ディスクファイルの選択」からISOイメージを選択。

Windowsのドライブレターの変更

  • 「コンピューターの管理」を起動
  • 「記憶域」>「ディスクの管理」を開く
  • ドライブを右クリックし「ドライブ文字とパスの変更」

ArduinoでGPS

GPSモジュールの出力

GPSモジュールのシリアル出力はNMEAフォーマット。
上記モジュールはデフォルトでは9600ボー。

ライブラリ

TinyGPS++を利用する。

Arduino

ログをPCで取得するならシリアルが2系統必要。
SoftwareSerialでもよいが、今回はSerial1のあるGR-CITRUSを使用。
Serial(USB)をPCとの通信、Serial1(TX:0, RX:1)をGPSモジュールとの通信に使用。

結線

GPSモジュール GR-CITRUS 備考
5V 5V GR-CITRUSから給電する。ボタン電池は不使用。
GND GND
RX 0 Serial1のTX。3.3Vレベルであることに注意(※) 今回は不使用。
TX 1 Serial1のRX。3.3Vレベルであることに注意(※)
1PPS 接続しない 1秒周期のパルス出力。今回は不使用。

※ 5V系Arduinoの場合はレベル変換が必要。


LED・電池

モジュールに給電するとまず橙色LEDが弱く点灯する。しばらく待ってモジュールが衛星を捕捉すると橙色LEDが1秒周期で点滅するようになる。
バックアップ電池(CR2032)を付けておくと衛星情報などが保持されて、再起動時の測位開始にかかる時間を短縮できる。

スケッチ

#include <TinyGPS++.h>
 
TinyGPSPlus gps;
 
void setup()
{
    Serial.begin(115200);
    Serial1.begin(9600); // 秋月のGPSモジュールはデフォルト9600ボー
    
    while (!Serial) { ; }
}
 
void loop()
{
    while (Serial1.available() > 0){
        char c = Serial1.read();
        // Serial.print(c);

        // シリアル受信データを1文字づつ食わせる
        gps.encode(c);
        // 位置情報が更新されたか?
        if (gps.location.isUpdated()){
            // 緯度・経度・標高を取得
            Serial.print("LAT=");  Serial.println(gps.location.lat(), 6);
            Serial.print("LONG="); Serial.println(gps.location.lng(), 6);
            Serial.print("ALT=");  Serial.println(gps.altitude.meters());
        }
    }
}

isValid(), isUpdated(), age()

  • isValid() : データが有効か?
  • isUpdated():データが更新されたか? (変化したとは限らない)
  • age():最後の更新からの経過時間 [msec]

取得できるデータ

// 位置
Serial.println(gps.location.lat(), 6);  // 緯度[度] (double)
Serial.println(gps.location.lng(), 6);  // 経度[度] (double)
Serial.print  (gps.location.rawLat().negative ? "S" : "N"); // 南北 (bool)
Serial.println(gps.location.rawLat().deg);       // 整数部          (uint16_t)
Serial.println(gps.location.rawLat().billionths);// 小数部(1/10億)  (uint32_t)
Serial.print  (gps.location.rawLng().negative ? "W" : "E"); // 東西 (bool)
Serial.println(gps.location.rawLng().deg);       // 整数部          (uint16_t)
Serial.println(gps.location.rawLng().billionths);// 小数部(1/10億)  (uint32_t)
// 日付
Serial.println(gps.date.value());       // 日付(DDMMYY)   (uint32_t)
Serial.println(gps.date.year());        // 年(西暦下2桁)  (uint16_t)
Serial.println(gps.date.month());       // 月(1-12)       (uint8_t)
Serial.println(gps.date.day());         // 日(1-31)       (uint8_t)
// 時刻
Serial.println(gps.time.value());       // 時刻(HHMMSSCC) (uint32_t)
Serial.println(gps.time.hour());        // 時(0-23)       (uint8_t)
Serial.println(gps.time.minute());      // 分(0-59)       (uint8_t)
Serial.println(gps.time.second());      // 秒(0-59)       (uint8_t)
Serial.println(gps.time.centisecond()); // 1/100秒 (0-99) (uint8_t)
// 速度
Serial.println(gps.speed.value());      // 速度[1/100ノット] (int32_t)
Serial.println(gps.speed.knots());      // 速度[ノット]      (double)
Serial.println(gps.speed.mph());        // 速度[マイル/時]   (double)
Serial.println(gps.speed.mps());        // 速度[m/s]         (double)
Serial.println(gps.speed.kmph());       // 速度[km/h]        (double)
// 進路(方位)
Serial.println(gps.course.value());     // 方位[1/100度] (int32_t)
Serial.println(gps.course.deg());       // 方位[度]      (double)
// 標高
Serial.println(gps.altitude.value());       // 標高[cm]       (int32_t)
Serial.println(gps.altitude.meters());      // 標高[m]        (double)
Serial.println(gps.altitude.miles());       // 標高[マイル]   (double)
Serial.println(gps.altitude.kilometers());  // 標高[km]       (double)
Serial.println(gps.altitude.feet());        // 標高[フィート] (double)
// 利用できる衛星の数
Serial.println(gps.satellites.value()); // (uint32_t)
// HDOP(水平精度低下率)
Serial.println(gps.hdop.value());       // (LSB=0.01 int32_t)

応用

NMEAの特定のフィールドのデータを抽出して取得することもできる。
だだし、数値であっても解釈されず文字列 (char*) として返す。

#include <TinyGPSPlus.h>

TinyGPSPlus gps;

// $GNGSA の 15~17個目フィールドを抽出するオブジェクト
// ($GPGSA:GPSのみ, $GNGSA: GPS+GLONASS等)
TinyGPSCustom pdop(gps, "GNGSA", 15);
TinyGPSCustom hdop(gps, "GNGSA", 16);
TinyGPSCustom vdop(gps, "GNGSA", 17);

void setup() 
{
    Serial.begin(115200);
    Serial1.begin(9600);
    while (!Serial) { ; }
}

void loop() 
{
    while (Serial1.available() > 0) gps.encode(Serial1.read());
    
    if (pdop.isUpdated() || hdop.isUpdated() || vdop.isUpdated())
    {
        Serial.print(F(" PDOP=")); Serial.print(pdop.value()); 
        Serial.print(F(" HDOP=")); Serial.print(hdop.value()); 
        Serial.print(F(" VDOP=")); Serial.println(vdop.value());
    }
}

参考記事


【追記】M5Stack用GPSユニットでも動作確認OK

電子工作のための電動ドライバー

電動ドライバーというと電気工事士さんとか工務店さんとかが使うでっかいやつをイメージするかもしれませんが、最近は一見すると手回しドライバーと変わらないような小型のものが出てきています。


電ドラボール

中でも人気なのが、国産老舗メーカー、ベッセルさんが出してる電ドラボールです。人気商品なのでレビュー記事や動画はたくさんありますが、やはり電気工事界隈やDIY界隈のかたが多いようです。この記事では比較的小型で精密なものを製作する電子工作にフォーカスしてレビューします。

さてこの電ドラボール、同社の定番商品であるボールグリップドライバーをそのまま電動にしたようなフォルムで手になじみます。締めるときは着座寸前まで電動で回し、その後手回しで本締めをするかんじです。電動ドライバーというより電動アシストドライバーと考えた方がいいかもです。なので木材に木ネジをゴリゴリ打ち込むような用途には向きません。電ドラボールが威力を発揮するのは、そんなに力は要らないけど長いネジを何本も締めたり外したりするだるい作業です。おもちゃの分解とか組み立てがすごく捗ります。

ちなみに、4倍速い(トルクは弱い) 電ドラボールハイスピードや、回転数とトルクを3段階で切り替えられる電ドラボールプラスも出たらしいんですが、私はそれらは持ってません。

プラスのビット (よく使うのはむしろ1番)

この電ドラボール、ビット1本付きセットとビット5本付きセットがあります。1本付きの場合、付属するビットはプラスの2番です。まあ、プラスの2番は電気工事や家具組み立てでいちばんよく使うビットなんですが、電子工作でよく使うのはむしろプラスの1番です。別途プラスの1番を買うといいでしょう。5本セットだとプラスの2番が2本も入ってます。プロにとってはビットは消耗品なのでしょうが、電子工作で使うぶんにはそうそうビットを使いつぶすことはないでしょう。プラスの3番とかマイナスも私はあまり使いませんね。

手持ちのビットを使う

ビットは一般的な6.35mmの六角軸のものであればワンタッチで挿せます。手持ちのビットがあればそれを使うのも良いでしょう。ただし、ラチェット用のビットで根っこにロック溝がないものはロックされないので簡単にすっぽ抜けます。すっぽ抜けると危険な場面では使わないでください。

六角ビット

個人的にはマイナスドライバーより六角のほうがよく使います。電子工作だと1.5ミリから4ミリくらいを使うことが多いんじゃないでしょうか。色分けされてるやつが便利です。

精密ビット

精密ドライバーのビットはせいぜいプラスのゼロ番までですね。私はゼロゼロ番はまず使いませんし、電動だとすぐにネジ頭をつぶしてしまいそうです。

ソケットビット

電子工作では六角ボルトはあまり使いませんが、ナットを締めることはよくあります。ただし、電子工作でよく使うM3やM2のナットに対応したソケットビットってあまり見かけません。ミニ四駆で使うタミヤのボックスドライバーのビットがM2用ですね。(ただしロック溝なし)

ちなみにソケットの二面幅(対辺)とネジ径との対応は下記資料を参照してください。M2が4mm、M3が5.5mmになります。

その他のビット

トルクスドライバー? なんですかそれは? 林檎信者じゃないので分かりませんね。
というのは冗談ですが、実際めったに使いませんね。

まあ、よく使うビットは人によって違うので、自分がよく使うビットを選んで買いそろえれば良いと思います。よく使うビットをホルダーに挿して引き出しに入れておくと捗ります。ホルダーはビットとセットで売ってる場合もあります。(上記の六角ビット7本組とか)

ドリルビット

電動ドライバーはドリルとしても使いたいですね。電ドラボールはパワーがあまり無いので大きな穴あけや固い素材は無理ですが、ちょっとした穴あけには使えます。私は電子工作では2ミリと3ミリをよく使います。まあ、2ミリ未満の穴あけにはピンバイスを使うでしょうし、きっちり穴を開けたいときはボール盤を使いますよね。

所感

けっきょくのところ、この電ドラボール1本でなんでもかんでもというわけにはいきません。ただ、今まで手回しドライバーでだるい思いをしてきた作業が断然快適になります。

メモ:雑草

「雑草という名の草はない 」とは使い古された言葉だけども、実際に雑草の名前を知っている人は少ない。

イネ目 (イネ科・カヤツリグサ科)

  • メヒシバ:茎が地面を這って広がり、立ち上がった茎に細長い穂を数本つけるやつ。
  • オヒシバ:メヒシバより太いやつ。
  • エノコログサ:いわゆる猫じゃらし。
  • スズメノカタビラ:背は低く、茎の先に白っぽい小さい穂をいくつもつけるやつ。
  • スズメガヤ:スズメノカタビラに似てるが茎が高く伸びるやる。
  • ホソムギ:茎に小さい穂を左右交互に並んでつけるやつ。
  • イヌムギ:茎からたくさん枝を出して小さい穂をつけるやつ。
  • カラスムギ:先にヒゲのある小さい穂をたくさんつけるやつ。穂は頭が垂れる。
  • チカラシバエノコログサより大きいブラシのような穂をつける。
  • セイバンモロコシ:やたら背が高くなり、茎から出た枝に赤っぽい穂をたくさんつけるやつ。
  • ハマスゲ:三角の茎の先端から数本の枝が出て、赤っぽい小さい穂をたくさんつけるやつ。
  • カヤツリグサ:ハマスゲによく似ていて、穂が黄色っぽいやつ。

キク科

  • タンポポ:だれでも知ってるやつ。
  • ノゲシタンポポより葉がイガイガで背の高いやつ。
  • タビラコ:細い茎に黄色い小さい花がたくさつ咲くやつ。
  • アキノノゲシ:葉はノゲシに似て、秋に白~淡黄色の小さい花をたくさんつけるやつ。
  • ノボロギク:タンポポより葉が菊っぽく、花は筒状のまま開かないやつ。
  • オオアレチノギク:葉と茎にうぶ毛が生えて、淡褐色の徳利状の小さい花をたくさんつけるやつ。
  • ヒメムカシヨモギ:オオアレチノギクに似て、小さい白い花をたくさんつけるやつ。
  • ハハコグサ:白い綿毛が生えてて、黄色い小さな花が密集して咲くやつ。ゴギョウのこと。
  • チチコグサ、チチコグサモドキ:ハハコグサより地味なやつ。違いがよく分からない。
  • アザミ:赤紫色の花をつける、トゲのあるやつ。
  • ハルジオン、ヒメジョオン:白い花をつけるやつ。違いがよく分からない。
  • セイタカアワダチソウ:茎が高く伸び、秋に黄色い小さな花をすごくたくさんつけるやつ。
  • ヨモギ:団子に練りこむやつ。花は秋咲くが目立たない。
  • ブタクサ:花粉症になるやつ。ヨモギに似てるけど葉の切れ込みがシャープ。

マメ科

その他の種子植物

  • アブラナ:いわゆる菜の花。
  • ナズナ:いわゆるぺんぺん草。
  • カタバミシロツメクサに似た形の葉で、黄色い小さな花が咲き、実から種が飛び散るやつ。
  • オオイヌノフグリ:たくさんの小さな葉と、青い小さな花が咲くやつ。
  • ツメクサ:細い爪のような小さな葉がたくさん生えて、背は低く、白い小さな花が咲くやつ。
  • ホトケノザ:シソを小さくしたような葉と、赤紫色の小さな花が咲くやつ。
  • オオバコ:さじ型のゴワゴワした葉が地面から放射状に生え、穂が立つやつ。
  • コニシキソウ:地面を這うように赤っぽい茎が広がり、左右対称に小さな葉がつくやつ。
  • コミカンソウ:コニシキソウに似てるが茎が立ち、葉の裏に小さな実がなるやつ。
  • チドメグサ:丸っこい小さな葉が地面を這うようにたくさん生えるやつ。
  • ツユクサ:先のとがった葉で、夏に青い花が咲くやつ。
  • ドクダミ:ハート型のやや紅色をおびた葉で、白い花びらに穂がつくやつ。
  • ヤブガラシ:ツルを伸ばし、黄緑と橙色の小さな花をブドウ状にたくさんつけるやつ。
  • カラスビシャク:緑色の仏炎苞から細い棒のようなものが伸びるやつ。
  • ウリクサ:地を這って広がり、卵形にギザギザのある葉をつけ、紫色の小さい花が咲くやつ。

シダ類

  • スギナ:ツクシがたけたあとに出てくるやつ。
  • トクサ:スギナよりザラザラしてるやつ。
  • いわゆるシダ:いわゆるシダっぽい葉のやつ。いろいろあるが見分けがつかない。

【参考記事】


VBAから使えるDLLの作成(VC++)

やりたいこと

VC++でDLLを作成し、ExcelVBAから呼び出す。
ただし、DLLはシステムに登録せず、Excellファイルと同じフォルダに置く。

DLLのプロジェクト作成

プロジェクトの新規作成で、言語に「C++」を選択し、「ダイナミック リンク ライブラリ(DLL)」を選択して、適当な名前のプロジェクトを作成する。ここではプロジェクト名を hoge とする。

DLLで公開する関数の定義

下記の要領で定義する。

extern "C" __declspec(dllexport) int __stdcall piyo(int foo)
{
    return foo * foo;
}
  • extern "C" は、C++形式でなくC言語形式のリンケージを指定
  • __declspec(dllexport) は、DLLで公開することを指定
  • __stdcall は、呼び出し規約(引数の渡し方)を指定

下記のようなマクロを定義しておくと簡潔になる。

#define DLLEXPORT    extern "C" __declspec(dllexport)

DEFファイルの記述

VC++アプリからDLLを使うなら上記の関数定義だけでよいが、VBAからVC++のDLLを使う場合は、DEFファイルを定義して公開関数名を宣言しておく。そうでないと _piyo@4 のような修飾がついた関数名になってしまう。

「プロジェクト」>「新しい項目の追加」で「Visual C++」>「コード」>「モジュール定義ファイル」を選択して、適当な名前のDEFファイルを作成し、下記の要領で記述する。

LIBRARY    "hoge"

EXPORTS
    piyo

【参考記事】

DLLをビルドする

Excelが32bit版か64bit版かに合わせてx86またはx64でビルドすること。

VBAでDLLの関数を宣言する

Excelが32bit版か64bit版かで異なるDLLの関数を宣言するには下記のようにする。

#If Win64 Then
Declare PtrSafe Function piyo Lib "hoge_64.dll" (ByVal foo As Long) As Long
#Else
Declare PtrSafe Function piyo Lib "hoge_32.dll" (ByVal foo As Long) As Long
#End If

【参考記事】

VBAの実行時にDLLをロードする

DLLはパスの通っているディレクトリにないと読み込めない。
今回のようにパスの通ってないディレクトリにDLLを置く場合には下記のようにしてDLLをロードする。
Excelが32bit版か64bit版かの区別にも注意。

Declare PtrSafe Function LoadLibrary Lib "kernel32.dll" _
            Alias "LoadLibraryA" (ByVal fileName As String) As Long

Declare PtrSafe Function FreeLibrary Lib "kernel32.dll" _
        (ByVal hLibModule As Long) As Long

#If Win64 Then
Const HOGE_DLL_FILENAME = "\hoge_64.dll"
#Else
Const HOGE_DLL_FILENAME = "\hoge_32.dll"
#End If
Dim lib_handle As Long

    ' DLLのロード
    lib_handle = LoadLibrary(ThisWorkbook.Path + HOGE_DLL_FILENAME )
    
    ' DLLのアンロード
    FreeLibrary (lib_handle)

VBAでDLLの関数を呼び出す

    Dim x As Integer
    Dim y As Integer
    x = Range("A1").Value
    y = piyo(x)
    Range("A2").Value = y