2026年2月18日 記
はじめに
TEditorMX ver1.80 で、ようやくx64用に更新することができました。
以前一度だけx64用コンパイルをやってみたことがあったのですが、
そのときはいろいろと理解不足のところがありまして、特に表示周りが上手くいかずに結局断念してしまいました。
で、今回、再チャレンジしてみたところ「何と!」あっさりと動いてしまいました。
今回上手くいった原因としては、
前回上手くいかなかった表示周りについてChatGPTに聞いてみたところ、
「ライブラリをDLLではなくてスタティックリンクすると不具合が解決するかもしれない」と、教えてくれました。
表示周りはバージョンの異なるDLLを使ってしまうと上手く動かないことがあるということでした。
そんな訳で、今回はDLLではなくてスタティックリンクでコンパイルしたことが上手くいった原因のひとつです。
それと不必要な(分かっている)ワーニングについてはすべて #pragma を使って抑制したこと。
「エラー」についてはソースコードを変更するしかないのですが、
可能な限り元のソースコードを弄(いじ)らずに、ちょっと強引な手法を使って修正しました。
もう少し具体的に書きますと、まず、
「エラー」が発生する根本的な原因は64bit化に伴いまして呼び出すAPIが変わっているものがあるのと、
更に引数の型が4バイト(32bit)から8バイト(64bit)に変わっているところがあるということです。
もうひとつはポインタ(ハンドル)が(一部の例外を除いて)32bitから64bitに変わります。
ポインタ(ハンドル)については問答無用の変更なので致し方ありません。
ちなみに使用言語はC言語で、使用しているのはC++コンパイラです。
C++の便利な拡張機能は利用していますが、基本的にはC言語の拡張言語のような感じでC++言語を使っています。
余談ですが、私の何となく覚えている記憶によりますと、
C++言語はC言語の後継言語として開発されたのですが、なぜC++という呼び名なのかと言いますと、
C++言語はあくまで「C言語の拡張言語」という位置付けだったためだと記憶しています。
(話を戻しまして)更についでに「並列コード生成」オプションも付加してコンパイルしました。
この「並列コード生成」オプションは、「OpenMP」と違いましてソースコードを変更することなく
コンパイラが勝手に並列コードを生成してくれるオプションスイッチです。
Windows用のプログラムで特にOpenMPとして記述する必要がないときには、簡単・便利に並列コードを生成(マルチコア)できるオプションスイッチです。
この「並列化コード生成」についても先に記述したDLLがらみで上手く動作しなかった(と思われる)機能のひとつです。
ライブラリをスタティックリンクしたおかけで正常に動作するようになりました。
以下に、win32 → x64用コンパイルするときに行った対策(変更)を記しておくことにします。
目次
(1)x64コンパイルで置き換えが必要になったAPI
(2)キャスト
(3)SendMessage API
(4)コンパイルスイッチ
(5)HIWORD、LOWORDマクロ
(6)OLEハンドル
(7)bool型ができた悪影響
(8)最後に
今回TEditorMXをwin32からx64に更新するにあたって置き換えが必要になったAPIを記しておきます。 なお、TEditorMXは基本的にWindows APIと標準ライブラリしか使っていません。
| 置換前のAPI | 置換後のAPI | その他 |
|---|---|---|
| DialogBox | DialogBoxParam | コールバック関数も変更が必要 |
| CreateDialog | CreateDialogParam | コールバック関数も変更が必要 |
| SetTimer | --- | コールバック関数の引数を修正 |
| SetWindowLong | SetWindowLongPtr | コールバック関数も変更が必要 |
更に SetWindowLongPtr APIについては、定数値も置き換える必要がありました。
| 置換前の定数値 | 置換後の定数値 | その他 |
|---|---|---|
| DWL_***** | DWLP_***** | ←このように置き換えます |
| DWL_USER | DWLP_USER | ←具体例1 |
| GWL_WNDPROC | GWLP_WNDPROC | ←具体例2 |
同様に変数の型も置き換える必要がありました。
今回変更した「型」は以下の通りです。
| 置換前の型 | 置換後の型 |
|---|---|
| INT | INT_PTR |
| int | |
| UINT | UINT_PTR |
| unsigned int | |
| LONG | LONG_PTR |
| long | |
| DWORD | DWORD_PTR |
| unsigned long |
上の表で「修正後の型」はwin32でコンパイルすると4バイト長、x64でコンパイルすると8バイト長になります。
パッと見た感じでは異常とも思えるような「キャスト」が必要でした・・・と言いますか、まともに替えるととんでもない作業量になりますし、
作業量が多くなるとミスなども増えてきます。結果としてまともに動かない・・・ということになりかねません。
・・・ということで、今の若者はこんなときどうするのか分かりませんが、私は少し強引な方法を取りました。
ちなみに、ここで言うところの「強引」とは、「文法的におかしい」というわけではなくて、
もともとC言語では許容範囲内の内容でして、ただダメなところは他者に見せると読みにくい(可読性が悪い・理解しにくい)ということだけです。
他の人に見せる予定がなければまったく問題にならない程度の内容です。
では、どのように変えたのか(キャストしたのか)を記します。
たとえばBOOL型変数(bool型変数ではない!)に4バイトから8バイトになった変数の値を代入しているところでは「切り詰め」が発生しますので具合が悪いです。
値を切り詰められてしまったときに、
切り詰められてしまった値でも「フラグ」として確実に機能するのであれば問題ありませんが、
それが保障されない場合には「大問題」になってしまいます。
このようなときにはBOOL型変数を他の8バイトの型に替えてしまい、
正しく「フラグ」として機能するようにする必要があります。
・・・このようなことになったときに「変数名」でそれが「フラグ」だと区別できるようになっていれば可読性を失うことはありません。
この辺(変数名の付け方)についてはプログラマ次第ですが、
私の場合はフラグとして用いる変数名の先頭には f の文字を付けるようにしていますので区別することが一応は可能です。
この辺をそんな感じで「強引」に変えてしまったので、他の人が見たらきっと混乱するかもしれませんね。
まぁ、他の人にソースコードを見せる(公開する)気はありませんのでその辺については問題ないですけどね。
戻り値は呼び出し時の引数により異なるりますが、 ポインタとかハンドルとか明らかに64bit(8バイト)になる戻り値以外は今まで通りの数値範囲と考えられます。 その辺でどのようにキャストするのか、切り詰めてしまっても問題ないのか、 それともそのままのLRESULT型として64bit(8バイト)変数型にキャストして扱うのかを判断すれば良さそうです。
x64でコンパイルする場合、コンパイルスイッチで指定する呼び出し規約は _cdesl (/Gd) でないとダメでした。 少なくとも _stdcall (/Gz) ではダメでした。
これらは x64 でも下位32bitのそれぞれ上位ワード、下位ワードが格納されます。 ちなみに1ワードは16bitです。
OLEハンドルはx64にしても32bitのままのハンドルでした。
下はWeb AIが教えてくれた 64bit - 32bit ハンドルの変換関数です。
この変換関数が正しいのかを私は判断できませんが、少なくともは今のところ問題なく動いています。
ちなみに「判断」できない理由ですが、
OLEハンドルがどの辺のアドレスを指し示すのか分からないので判断できない・・・というのが理由のひとつです。
OLEハンドルが4バイトということはその範囲内に必ず配置されるのでしょうけれど、
具体的な仕様とか私は知りませんので判断する余地もありません。
もう少し突っ込んで書きますと、アプリケーションソフトで扱われるアドレスはMMUが変換した「論理アドレス」です。
MMUがどのように物理アドレスを論理アドレスに変換しているのか、
OLEハンドルがどのように変換されているのか不明なのです。
ちなみに「ハンドル」とはポインタ型変数のアドレスを格納しているポインタ型変数で、
C言語風に言えば「ポインタのポインタ」と言い換えることができます。
OS(MMU)によってメモリ上のデータの再配置が行われ、
アドレスが入れ替わってしまったときでも正しく目的のアドレスを指し示すことができるように工夫された変数と言えます。
ご参考まで。
// OLE_HANDLE と一般的な HANDLE をコンバートする関数 // Convert HANDLE to OLE_HANDLE safely OLE_HANDLE HandleToOleHandle(HANDLE h) { return static_cast(reinterpret_cast (h)); }
// OLE_HANDLE と一般的な HANDLE をコンバートする関数 // Convert OLE_HANDLE back to HANDLE HANDLE OleHandleToHandle(OLE_HANDLE oh) { return reinterpret_cast(static_cast (oh)); }
C++コンパイラで新たにbool型が儲けられたことで整数変数をフラグと見なすことができなくなりました。
→ 下のコードの「エラー」の行ようなことをしようとするとエラーになります。
→ 下のコードは期待した値が変数fに入るので、今のところの Visual Studio C++コンパイラでは問題なさそうです。
// test code start int d, f; d = 10; // f = ! d; // エラー f = (bool)! d; // f=0、キャストすれば何とか通るようだ。 d = 0; f = (bool)! d; // f=1 // test code end
以上が今回のx64化で行った対策です。
C++の機能を使うとトラブったときに意味不明なエラーが沢山出てくるので理解に苦しみますが、
上手く使えばすごく便利で強力な機能なのだろうと思います。
C++言語をもっと上手く使えるようになれば良いのかもしれませんね。