帳票出力の新関数
PRNFILE* lpt = prn_open(L"OKI MICROLINE XXXX");
prn_printf(lpt, "\x1b@"); // 初期化
prn_printf(lpt, "test"); // 印字
prn_printf(lpt, "\f"); // 改ページ
prn_close(lpt);
---------------------------------------------------------
#include <windows.h>
#include <cstdarg>
struct PRNFILE {
HANDLE hPrinter;
};
----------------------------------------------------------
PRNFILE* prn_open(const wchar_t* printerName)
{
PRNFILE* pf = new PRNFILE;
if (!OpenPrinterW*1 {
delete pf;
return nullptr;
}
DOC_INFO_1W docInfo;
docInfo.pDocName = (LPWSTR)L"Raw Print";
docInfo.pOutputFile = NULL;
docInfo.pDatatype = (LPWSTR)L"RAW";
StartDocPrinterW(pf->hPrinter, 1, (LPBYTE)&docInfo);
StartPagePrinter(pf->hPrinter);
return pf;
}
-------------------------------------------------------
int prn_printf(PRNFILE* pf, const char* fmt, ...)
{
char buffer[1024];
va_list args;
va_start(args, fmt);
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
if (len <= 0) return 0;
DWORD written;
WritePrinter(pf->hPrinter, buffer, len, &written);
return written;
}
-------------------------------------------------------
void prn_close(PRNFILE* pf)
{
if (!pf) return;
EndPagePrinter(pf->hPrinter);
EndDocPrinter(pf->hPrinter);
ClosePrinter(pf->hPrinter);
delete pf;
}
------------------------------------------------------
| 旧DOSコード | 置換後 |
|---|---|
FILE* lpt |
PRNFILE* lpt |
fopen() |
prn_open() |
fprintf() |
prn_printf() |
fcloseall() |
prn_close() |
まとめ(重要)
*************************
完成版 prn_printf()(実務用)
#include <cstdarg>
#include <cstdio>
#include <windows.h>
int prn_printf(PRNFILE* pf, const char* fmt, ...)
{
if (!pf) return 0;
char buffer[2048]; // 帳票ならこれで十分
va_list args;
va_start(args, fmt);
int len = vsnprintf(buffer, sizeof(buffer), fmt, args);
va_end(args);
if (len <= 0) return 0;
if (len >= (int)sizeof(buffer)) {
// バッファ溢れ防止(切り捨て)
len = sizeof(buffer) - 1;
}
DWORD written = 0;
WritePrinter(pf->hPrinter, buffer, len, &written);
return written;
}
④ 実務例(帳票コードが完全にそのまま)
int value = 123;
prn_printf(lpt, "\x1b@");
prn_printf(lpt, "数量:%6d\n", value);
prn_printf(lpt, "金額:%6d 円\n", value * 10);
prn_printf(lpt, "\f");
注意点(ここだけは必ず守る)
⚠ ワイド文字は使わない
❌ ダメ
⭕ OK
ESC/Pは char(Shift-JIS)前提
⚠ 改行コード
| 文字 | 意味 |
|---|---|
\n |
行送り |
\r |
復帰 |
\r\n |
機種によりズレ |
*1:LPWSTR)printerName, &pf->hPrinter, NULL
OKI ESC/P コマンド具体例
OKI(MICROLINE系)で RAW印刷 × ESC/P を前提に、実務でよく使うコマンドだけに絞ります。
※ 機種差はありますが、MICROLINE 5xxx / 8xxx 系はほぼ共通です
0️⃣ 基本(必ず最初に送る)
-
状態リセット
-
文字ピッチ・改行状態クリア
-
最初の1発は必須
1️⃣ 印字+改ページ(最小テスト)
✔ これが出なければ RAWが通っていない
2️⃣ 改行・行送り制御
| 内容 | コマンド |
|---|---|
| 改行 | \n |
| キャリッジリターン | \r |
| 1/6インチ改行 | \n |
| n/216インチ行送り | ESC J n |
3️⃣ 文字ピッチ(超重要)
| 内容 | コマンド |
|---|---|
| 10cpi | ESC P |
| 12cpi | ESC M |
| 17cpi | ESC g |
| 20cpi | ESC W 1(機種依存) |
4️⃣ 強調・装飾
太字
下線
5️⃣ 日本語(漢字)印刷【重要】
OKIは Shift-JIS + 漢字モード が基本。
⚠ UTF-8不可
⚠ char[] で送る
6️⃣ 横位置制御(桁合わせ)
絶対位置
タブ設定
7️⃣ 用紙サイズ・マージン
左マージン
右マージン
8️⃣ 罫線(帳票で必須)
※ 半角カナ・コードページ依存
OKIは IBM拡張文字が安定
9️⃣ 実務用サンプル(帳票1枚)
✔ OKI帳票印刷の王道構成
🔟 トラブル回避ルール(重要)
| NG | 理由 |
|---|---|
| UTF-8送信 | 文字化け |
| GDI印刷 | ESC無効 |
| 改ページ無し | 紙詰まり |
| 初期化省略 | 設定引き継ぎ |
最後に(実務者向け助言)
-
ESC/Pは 「送った順=状態」
-
毎ジョブ初期化する
-
帳票は 固定ピッチ前提
C++プリンタ出力
結論から言うと、そのコードをそのままでは「USB接続プリンタ」には直接出力できません。fopen("lp.txt","w") は 単なるテキストファイル出力 で、MS-DOS時代の LPT1: 直書きとは意味が違います。
以下、Windows + USB接続 OKIプリンタで同等のことをするための「現実的な手順」を段階的に説明します。
① まず今のコードが何をしているか
これは
-
lp.txtというファイルを作成 -
ESC/P 系の制御コードを含む 生データを書いている
だけです。
👉 USBプリンタには一切届いていません
② USBプリンタに送る方法は3つある
方法A:Windows印刷API(推奨・正攻法)
-
OpenPrinter -
StartDocPrinter -
WritePrinter
👉 ESC/P制御コードをそのまま送れる
👉 OKIプリンタ向き
👉 C++で制御可能
方法B:RAW印刷としてファイルを送る
-
lp.txtを作る -
copy /b lp.txt "\\spool\OKIプリンタ名"
👉 手動 or system()
👉 簡易だが運用向きではない
方法C:LPTポートに仮想割り当て(非推奨)
-
USBプリンタを LPT1: に割り当てる
-
古い互換用途のみ
👉 Windows10/11では不安定
👉 今はほぼ使われない
③ 一番確実な方法:Windows RAW印刷(方法A)
🔧 手順概要
④ 実際のC++サンプルコード(コンソールアプリ)
⑤ 重要ポイント(OKIプリンタの場合)
✔ ドライバは「汎用テキスト / RAW対応」になっているか
-
OKI純正ドライバでもOK
-
「RAW印刷」を受け付ける必要あり
✔ ESC/P対応機種か
-
OKI MICROLINE系はほぼ対応
-
印字データは
char(ANSI) -
プリンタ名は
wchar_t
⑥ どうして fopen("LPT1:") はダメなのか?
Windowsでは 必ずスプーラ経由 が基本です。
⑦ まとめ(最短ルート)
✅ USB接続OKIプリンタ
✅ ESC/Pコードを直接送りたい
もし次に進むなら:
-
プリンタ名の取得方法
-
OKIドライバ設定(RAW有効化)
-
日本語(漢字)印刷時の注意点
どこまでやりたいか、教えてください。
--------------------------------------------------------------------------------
追加方法の選択(ここ重要)
✔ ローカルプリンターとして追加
-
「ローカル プリンターまたはネットワーク プリンターを手動で追加」
-
次へ
✔ 既存ポートを使う
-
ポート:
USB001 (仮想プリンタポート)
※数字は環境依存
ドライバ選択
-
メーカー:OKI
-
プリンタ:機種名を正確に
※ なければ
「ディスク使用」 → OKIドライバフォルダ指定
手順⑤ プリンタが出たら必ず確認する設定
プリンターのプロパティ → 詳細設定
-
スプールの処理方法
-
☑ プログラムが印刷を完了するまでスプール
-
-
-
☑ RAW
-
👉 ここが RAW 以外だと ESC/P が無効
手順⑥ それでも出ない場合(強制確認)
PowerShellで確認
出るがプリンタ一覧に出ない
→ ドライバ破損 or 関連付け失敗
最終手段(確実)
-
OKIドライバ アンインストール
-
USBケーブル 抜く
-
PC再起動
-
ドライバ 管理者で再インストール
-
指示が出るまでUSBを挿さない
👉 これで9割直ります。
実務視点のアドバイス(重要)
-
USBは「挿しただけ」ではダメ
-
プリンタ一覧に出て初めて OpenPrinter() が使える
-
USBポート名(USB001)は関係ない
-
プリンタ名がすべて
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
① プリンタは出るが OpenPrinter() が失敗する
よくある原因
即効対処
✅ プリンタ名を再確認
C++:
※ 空白・全角半角も一致必須
✅ Unicode版APIを使う
✅ 管理者権限で実行
-
Visual Studio → exe → 管理者として実行
✅ 失敗理由を取得
代表例:
| エラー | 意味 |
|---|---|
| 5 | アクセス拒否 |
| 1801 | プリンタ名不正 |
② 印刷は出るが制御コード(ESC/P)が効かない
よくある原因
-
RAW印刷になっていない
-
ドライバがGDI変換している
-
ESC/P非対応モード
即効対処
✅ プリンタのプロパティ
詳細設定タブ
-
データ形式 → RAW
-
☑ プログラムが印刷を完了するまでスプール
✅ OKIドライバ設定
-
印刷設定
-
エミュレーション:ESC/P または EPSON
-
PCL / PS は不可
✅ RAW指定をコードで明示
✅ テスト用最小データ
これで初期化+印字+改ページが効けばOK。
③ 日本語が文字化けする
よくある原因
-
文字コード不一致
-
フォント未指定
-
プリンタ漢字ROM未対応
即効対処(OKI向け)
✅ Shift-JISで送る
※ OKIのESC/Pマニュアル参照
✅ Windows側で文字変換しない
-
wchar_tは使わない -
UTF-8 は送らない
✅ フォント指定(英数のみなら不要)
④ 印刷されるが途中で止まる/真っ白
原因
-
StartPagePrinter()/EndPagePrinter()不足 -
フォームフィード未送信
-
データ長ミス
対処
※ sizeof ではなく strlen を使うケースも確認
⑤ 何も出ない(無反応)
原因
-
プリンタがオフライン
-
キュー詰まり
-
ドライバ破損
対処チェックリスト
-
印刷キューを全削除
-
プリンタ再起動
-
ドライバ再インストール
-
テストページ印刷成功確認
⑥ 動作確認の「黄金テスト手順」
-
Windowsテストページが出る
-
PowerShellでプリンタ名確認
-
C++で
"Hello\f"をRAW送信 -
ESC/P制御を足す
-
日本語を足す
👉 1つずつ積み上げる
まとめ(実務的結論)
| 症状 | 最重要ポイント |
|---|---|
| OpenPrinter失敗 | 名前・権限 |
| 制御効かない | RAW + ESC/P |
| 文字化け | Shift-JIS |
| 無反応 | ドライバ |
ここまで理解できていれば、USBプリンタをDOS感覚で直接叩く用途はほぼ制圧です。
RS-232C送受信(3/3)
int TxCommand(const char *str)
{
int f = 0, state = 0, retry = 0;
int c, n, lrc;
clock_t t;
t = clock();
while (f == 0) {
switch (state) {
case 0: // ENQ送信
if (retry < 2) {
retry++;
SioSend(ENQ);
t = clock();
state = 1;
} else {
f = 2;
}
break;
case 1: // ACK/NAK待ち
while *1 == 0) {
if (t + 1000 < clock()) {
state = 0;
break;
}
}
if (c == ACK) {
retry = 0;
state = 2;
} else if (c == NAK) {
state = 0;
}
break;
case 2: // データ送信
if (retry < 2) {
retry++;
SioSend(STX);
lrc = 0;
for (n = 0; str[n]; n++) {
lrc ^= str[n];
SioSend(str[n]);
}
SioSend(IntToHex(lrc >> 4));
SioSend(IntToHex(lrc & 0x0F));
SioSend(ETX);
t = clock();
state = 3;
} else {
f = 3;
}
break;
case 3: // ACK/NAK待ち
while *2 == 0) {
if (t + 2000 < clock()) {
state = 2;
break;
}
}
if (c == ACK) {
state = 4;
} else if (c == NAK) {
state = 2;
}
break;
case 4:
SioSend(EOT);
f = 1;
break;
}
}
return f - 1;
}
int RxCommand(char *str)
{
int state = 0, c, f = 0, cnt = 0;
unsigned int lrc, lrc0;
clock_t t = clock();
while (f == 0) {
switch (state) {
case 0:
while *3 == 0) {
if (t + 5000 < clock()) {
f = 2;
break;
}
}
if (c == ENQ) state = 1;
break;
case 1:
SioSend(ACK);
t = clock();
state = 2;
break;
case 2:
while *4 == 0) {
if (t + 1000 < clock()) {
state = 0;
break;
}
}
if (c == STX) {
cnt = 0;
t = clock();
state = 3;
}
break;
case 3:
while *5 == 0) {
if (t + 1000 < clock()) {
state = 0;
break;
}
}
if (c == ETX) {
state = 4;
} else {
str[cnt++] = c;
}
break;
case 4:
str[cnt] = '\0';
lrc0 = (HexToInt(str[cnt-2]) << 4) | HexToInt(str[cnt-1]);
str[cnt-2] = '\0';
lrc = 0;
for (int i = 0; str[i]; i++) lrc ^= str[i];
if (lrc != lrc0) {
SioSend(NAK);
} else {
SioSend(ACK);
}
state = 5;
t = clock();
break;
case 5:
while *6 == 0) {
if (t + 1000 < clock()) {
f = 3;
break;
}
}
if (c == EOT) f = 1;
break;
}
}
return f - 1;
}
int main()
{
if (!OpenComPort("\\\\.\\COM1")) return 1;
char buf[256];
TxCommand("HELLO");
RxCommand(buf);
printf("RX:%s\n", buf);
CloseHandle(hCom);
}
RS-232C送受信(2/3)
// シリアルポートオープン
#include <windows.h>
#include <stdio.h>
#include <time.h>
HANDLE hCom;
bool OpenComPort(const char* portName)
{
hCom = CreateFileA(
portName,
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
OPEN_EXISTING,
0,
nullptr
);
if (hCom == INVALID_HANDLE_VALUE) {
printf("COMポートオープン失敗\n");
return false;
}
DCB dcb = {0};
dcb.DCBlength = sizeof(DCB);
GetCommState(hCom, &dcb);
dcb.BaudRate = CBR_9600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
SetCommState(hCom, &dcb);
COMMTIMEOUTS to = {0};
to.ReadIntervalTimeout = 1;
to.ReadTotalTimeoutMultiplier = 1;
to.ReadTotalTimeoutConstant = 1;
SetCommTimeouts(hCom, &to);
return true;
}
// 1バイト送信(SioSend 相当)
void SioSend(unsigned char c)
{
DWORD written;
WriteFile(hCom, &c, 1, &written, nullptr);
}
// 1バイト受信(SioRecieve 相当)
int SioReceive()
{
unsigned char c;
DWORD read;
if (ReadFile(hCom, &c, 1, &read, nullptr) && read == 1) {
return c;
}
return 0; // 受信なし
}
RS-232C送受信(1/3)
これは MS-DOS時代のBIOS/独自ドライバ前提のRS-232C制御なので、
Windows 10 では Win32 API(CreateFile / ReadFile / WriteFile)で置き換えるのが正解です。
以下では方針 → 最小構成の通信ラッパ → TxCommand / RxCommand の移植例、という順で説明します。
(通信プロトコルのロジックは極力そのままにしています)
全体方針(重要)
-
SioSend()→ WriteFile -
SioRecieve()→ ReadFile(1バイト) -
COM1→"\\\\.\\COM1"(Windows表記) -
タイムアウト → COMMTIMEOUTS + clock()
-
コンソールアプリでOK(MFC不要)
制御文字(明示しておく)
1️⃣ RS-232C 基本ラッパ(Windows10用)
シリアルポートオープン
1バイト送信(SioSend 相当)
1バイト受信(SioRecieve 相当)
2️⃣ TxCommand(Windows版)
3️⃣ RxCommand(Windows版)
4️⃣ main例
補足(重要)
C++プリンタ
Windows 10 + OKI(USB) なら、一番安全で確実な方法はこれ一択です。
結論(最短ルート)
👉 「Generic / Text Only」プリンタを追加して、そこに生データ(ESC制御)を流す
これで
のような プリンタ制御コードがそのまま有効になります。
手順①:Generic / Text Only プリンタを追加
1️⃣ プリンタ追加
-
設定
-
デバイス
-
プリンターとスキャナー
-
プリンターまたはスキャナーを追加
※ 一覧に出てこなければ
👉 「プリンターが一覧にない場合」
2️⃣ 手動で追加
-
ローカルプリンターまたはネットワークプリンターを手動設定で追加
-
既存のポートを使用
-
USB001(仮想プリンタポート)
-
3️⃣ ドライバー選択
-
メーカー:Generic
-
プリンタ:Generic / Text Only
名前例:
手順②:Windows側の重要設定(必須)
プリンタのプロパティ
-
デバイスとプリンター
-
OKI_TEXT を右クリック → プリンターのプロパティ
📌 詳細設定タブ
-
印刷プロセッサ →
WinPrint -
既定のデータの種類 → RAW
👉 これが超重要(ESCコードを壊さない)
手順③:印刷方法
方法A:ファイル経由(おすすめ)
あなたの既存コードを 一切変更しない方法です。
印刷コマンド
※ /b を必ず付ける(制御コード保護)
方法B:アプリから直接送る(少し上級)
※ 権限や環境依存が強い
👉 方法Aの方が安定
OKIプリンタでの注意点(重要)
-
OKIは ESC/P互換モード または テキストモード が必要な場合あり
-
機種によっては
-
ページ記述言語:ESC/P
-
Emulation:EPSON
を設定する
-
👉 プリンタ本体メニューで確認
動作確認用ミニテスト
-
正常 → 印字されて紙が送られる
-
文字化け → エミュレーション設定不一致
まとめ(迷ったらこれ)
| 項目 | 設定 |
|---|---|
| OS | Windows 10 |
| プリンタ | OKI USB |
| ドライバ | Generic / Text Only |
| 印刷方法 | copy /b |
| データ形式 | RAW |
| 制御コード | ESC/P 有効 |
ここまでやって 印字されない/制御が効かない場合は、
-
OKIの 正確な型番
-
今出している 制御コード一覧
を出してください。
機種別に ESC/P → OKI 向けに最適化します。