ラベル VisualStudio の投稿を表示しています。 すべての投稿を表示
ラベル VisualStudio の投稿を表示しています。 すべての投稿を表示

2024年12月29日日曜日

VisualStudio Installer プロジェクトでコードサイン証明書を適用する

pfx形式が使えなくなった

製品のインストーラ(exe形式ファイル)を作成するために、これまで頑なに Install Shield 2015 の無料版を使用していましたが。
コードサイン証明書が変更になってしまい、pfx形式だとうまくビルドできなくなってしまいました。
そこで、VisualStudio 2022 の Installer プロジェクトを新たに作成することにしました。

ビルドしたインストーラファイル(msi形式ファイル)にコードサイン証明書を適用する必要があるのですが、その手順を自動化したので紹介します。

USBドングルでの証明書

これまでは、コードサイン証明書は .csr 形式のファイルで提供されていて、そいつを元に .pfx 形式ファイルを作成していました。
インストーラプロジェクトでは、sign タブの部分で pfx ファイルとパスワードを設定さえすればビルドできてたんですよね・・・
Visual Studio 2015の Install Shield では、ストアに登録した新しいコードサイン証明書を指定しても、エラーになってビルドできませんでした。

Installer Project を作成する

VisualStudio 2022 の Installer プロジェクトを作ります。
プロジェクトのプロパティで必要事項を設定するのですが、設定項目が Install Shield とほぼ同じのため、さほど混乱はしませんでした。
重要場部分としては、次のような点でしょうか。

  1. ProductCode はバージョン番号を変えたら必ず新しいものに変更する。
  2. UpgradeCode は変更してはならない。(そうしないとインストール時に以前のバージョンをアンインストールしてくれなくなります)
  3. ProductName 部分はインストール先のフォルダ名に利用される点に注意
  4. Version は##.##.####形式でなければならない
これまでバージョン番号には ##.##.##.## の形式だったのが使えなくなりました。
後述のアンインストールしてくれない問題は、ここが原因なのかもしれません。

signtool.exe を使ってコードサイン証明書を適用

無事インストーラが出来上がったら、コードサイン証明書を適用します。
signtool.exe の GUI で使用するのですが、入力項目が多く、とっても面倒でした。
ということで、インストーラプロジェクトの PostBuildEvent に次の行を追加して自動化しました。

signtool sign /a /n "証明書のトークン名" /t "タイムスタンプサーバのURL" /d "製品記述" /du "製品のURL" $(BuiltOuputPath)

途中でトークンパスワードを入力する手間が発生するけど。GUIでごにょごにょ入力するよりは、はるかにマシになりました。

以前のバージョンをアンインストールしてくれません。

UpgradeCode は正しく設定しているにもかかわらず、インストーラが以前のバージョンを事前にアンインストールしてくれなくなりました。
いろいろ試したり調べたりしたけど、どうしてもだめでした・・・。
バージョンの表現形式が変わってしまったからなのかもしれません・・・
あるいは、exe形式から msi形式に変わったからなのか?

もし情報をお持ちの方いらっしゃいましたら、教えてくださいませ。

まとめ

ついに Install Shield を脱却しましたw
めでたしめでたし。

2024年11月17日日曜日

SQLiteを使ったプログラムで Out of memory 例外が発生する

メモリいっぱいあるのに、Out of memory

C# で開発したファイル処理プログラム。
SQLite を使ってインデックスを貼り高速検索するようにしてます。
SQLite の便利な機能に、データベースをメモリ内で処理させるってのがあります。

   SqlConnectionSb = new SQLiteConnectionStringBuilder { DataSource = ":memory:" };
   SqlConnection = new SQLiteConnection(SqlConnectionSb.ToString());
  
こうすることで、オンメモリ処理になり、さくさく動くようになります。
DataSource をハードディスク上のファイルに置き換えることもできますが、それだと遅くて使い物にならないような機能のプログラムなんです。
このプログラムにちょっとばかり大きなファイルを処理させると、途中で Out of memory 例外でエラーになって落ちてしまう問題が出ました。
どーして?
タスクマネージャで見ててもメモリの空き容量は十分にあるのに。
そんな、ばなな。

キャッシュを大きくしてみたり

SQLite out of memory で検索しても、どういうわけかなにもヒットするページがありません。
しかたなく接続時のパラメータを変更してみたり。

SqlConnectionSb = new SQLiteConnectionStringBuilder { DataSource = ":memory:", CacheSize = 10000 };
PageSize とか MaxPageCount といったパラメータも変更してみたけど、効果なし・・・困りました。

32ビットを選ぶ?

Visual Studio のプロジェクトは「AnyCPU」としていて、32bitでも64bitでも動作するようにしてるつもりだったけど。
プロジェクトプロパティ「ビルド」の項目に、「✅32ビットを選ぶ」ってのがありました。

なんじゃ、こりゃあ?ってなくらい、知りませんでしたよ。(笑)
このオプションのせいで、AnyCPU が 32bit プロセスとしてビルドされてしまうわけです。
これまでリリースしてたプロダクト、全部再確認が必要になりましたよw

上記チェックボックスをOFFにして、64bitOSで動作させたら、Out of memory例外は出なくなりました。
あ、プロジェクトプロパティは Debug と Release の両方の構成を変えなきゃならないから、注意してくださいまし。

今回の問題は、問題を再現するのにもトライアンドエラーするのにも1時間以上かかるので、大変でした。

ま、解決できてよかった。
めでたしめでたし。

2023年6月2日金曜日

Windows hosts ファイルを編集して名前解決させる。

hosts

Virtual Machine で開発することが圧倒的に多くなってきましたね。
本番環境と開発環境を分けるためにドメインの名前解決を一時的に変更することも多々あります。
そこで必要になるのが hosts の編集です。

hosts を編集して名前解決させるには

これが、なかなかめんどくさいw
1.メモ帳を管理者モードで起動
2.ファイルを開くで、右下のフィルタを「すべてのファイル」に変更。
3.C:\Windows\System32\drivers\etc\hosts を開いて編集。
4.保存。
5.コマンドプロンプトを開いて
6.C:\> ipconfig /flushdns

一日一回とかならまだいいけど、数回実施するとなるとめんどくさいね。
ということで、プログラム書いてみましたw

HostsEditor

今回は、Visual Studio 2022 Comunity の C# Windows Form .NET プロジェクトを作成しました。
Form1 には、保存終了用のツールボタンと、全画面に Dock した TextBox を配置します
TextBox のプロパティは、Multiline=true, MaxLength を大きめに。font も大きめに、程度かな。
Name は editText と付けておきました。

Form1.cs

プログラムは、こんな感じです。
hosts ファイルの位置を保持
全行を読み込んで TextBox に入れて編集可能にする
保存ボタンが押されたら、保存して、CMD.exe で ipconfig /flushdns を実行。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace HostsEditor
{
    public partial class Form1 : Form
    {
        internal string HostsFileName { get; set; }
        public Form1()
        {
            InitializeComponent();
        }

        //初期表示
        private void Form1_Load(object sender, EventArgs e)
        {
            // ファイル名
            string system_folder = Environment.GetFolderPath(Environment.SpecialFolder.System);
            string hosts_path = Path.Combine(system_folder, "drivers", "etc");
            HostsFileName = Path.Combine(hosts_path, "hosts");

            System.IO.FileInfo fi = new System.IO.FileInfo(HostsFileName);
            if(4194304 < fi.Length)
            {
                MessageBox.Show("hosts ファイルが大きすぎます。他の手段で編集してください。",
                    "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                Close();
            }
            // ファイルを 65,536 byte まで読み込む
            string[] lines = File.ReadAllLines(HostsFileName);
            string text = String.Join("\r\n", lines);
            editText.Text = text;
            // 全選択を解除
            editText.SelectionStart = 0;
        }

        private void closeClick(object sender, EventArgs e)
        {
            try
            {
                // 保存
                string text = editText.Text;
                using (StreamWriter streamWriter = new StreamWriter(HostsFileName))
                {
                    // Writeメソッドで文字列データを書き込む
                    streamWriter.Write(text);
                    // StreamWriterオブジェクトを閉じる
                    streamWriter.Close();
                }
                // ipconfig /flushdns を実行。完了まで待つ。
                System.Diagnostics.Process process = new System.Diagnostics.Process();
                //ComSpec(cmd.exe)のパスを取得して、FileNameプロパティに指定
                process.StartInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
                //出力を読み取れるようにする
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardInput = false;
                //ウィンドウを表示しないようにする
                process.StartInfo.CreateNoWindow = true;
                //コマンドラインを指定("/c"は実行後閉じるために必要)
                process.StartInfo.Arguments = @"/c ipconfig /flushdns";
                //起動
                process.Start();
                //(親プロセス、子プロセスでブロック防止のため)
                process.WaitForExit();
                process.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            // 画面を閉じる
            Close();
        }
    }
}

管理者として起動

プログラムを管理者として起動する必要があるので、Program.cs を編集します。
Main部分に以下の内容を追記します。

        static void Main(string[] args)
        {

            // 管理者権限に昇格させて自分自身を起動する
#if (!DEBUG)
            Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
            var pri = (WindowsPrincipal)Thread.CurrentPrincipal;
            //管理者権限以外での起動なら, 別プロセスで本アプリを起動する
            if (!pri.IsInRole(WindowsBuiltInRole.Administrator))
            {
                var proc = new ProcessStartInfo()
                {
                    WorkingDirectory = Environment.CurrentDirectory,
                    FileName = Assembly.GetEntryAssembly().Location,
                    Verb = "RunAs"
                };

                if (args.Length >= 1)
                    proc.Arguments = string.Join(" ", args);

                //別プロセスで本アプリを起動する
                Process.Start(proc);

                //現在プロセス終了
                return;
            }
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
#endif
        }

まとめ

DOBON.NET さんの記事を参照しました。
デバッグ時は、VisualStudio を管理者モードで起動する必要があります。
わりと、楽になりましたw
めでたしめでたし。

2020年11月28日土曜日

「マネージとネイティブの境界の前でハンドルされませんでした」という例外でハマった。

 開発中のDLLをデバッグしているときに、「型 'System.Runtime.InteropServices.COMException' の例外が XXX.dll で発生しましたが、マネージとネイティブの境界の前でハンドルされませんでした」
という例外が出るようになって、非常に困ったので、まとめ。



例外が出るのはいいとして、

□この種類の例外がスローされると中断します
のチェックボックスをOFFにしても、必ず中断されてしまうから困ったのです。

しかも、ですよ。
catch(Exception e){}のブロックに入ってくれない。
最もひどい状態では、同じ部分で延々と例外のダイアログが表示され続けるために
「デバッグの中止」
を使ってプロセスを落とすしかなくなるんです。

ちなみに開発環境は Visual Studio 2015 です。
開発しているのは、Office Outlook 用のアドインDLL
メールのヘッダに独自に設定している拡張ヘッダがあるかどうかを調べるコードです。
一般のメールにはそのヘッダはないので、「そんなヘッダはありません」という例外が出ます。(それはいい)
try{}catch{}ブロックでその例外を無視すればいいだけのコードです。

まず、「catch(Exception e){}のブロックに入ってくれない。」件に関しては、プロジェクトのProperies 設定の「デバッグ」で
□ネイティブコードのデバッグを有効にする
をチェックすることで、catch{}ブロックに入ってくるようになりました。


しか~し。
□この種類の例外がスローされると中断します
のチェックボックスをOFFにしても必ず例外で中断する現象は変わりません。

開発環境によってはこの問題が発生しないので、上手くいく環境といかない環境を比較しながら調べてみました。
結論:「ツール」-「オプション」-「デバッグ」で
□例外が AppDomain またはマネージ/ネイティブの境界を超える場合にブレークする(マネージのみ)
を OFF にします。



以上で例外での中断がなくなりました。
この問題の解決にもけっこうな日数かかりました。orz

2020年6月18日木曜日

Windows上でPerlの統合環境(Visual Studio Code + Strawberry Perl)

WEB上の情報を参考に、Windows上でPerlの統合環境を作れないか調べてみると、以下の組み合わせが便利そうだったので、構築してみました。

1.Visual Studio Code
2.Strawberry Perl
3.Perl Debugger

まずは Visual Studio Code (https://code.visualstudio.com/)のダウンロードとインストール。
使いやすいように、表記を日本語にしておきます。

次に Strawberry Perl のインストールです。
WEB上には PowerShell を使ってインストール、って紹介もあったけど、うちの環境ではうまくインストールできなかったので、公式サイト(http://strawberryperl.com/)からダウンロードしてインストールしました。
インストールだけで PATH も変更されたようです。

Perl Debugger は、VS Code を起動して、「表示」「拡張機能」で検索窓に perl って入力したら「Perl Debugger」が出てくるので、それをインストールしました。

これで準備完了。

さっそく sample.pl を作成。
#! /usr/bin/perl
use strict;

print "Hellow Perl\n";

exit;
  

print 行にブレークポイントを貼って実行すると、見事にブレークしてくれました。(^_^)v

さて、よくある問題の日本語表示を試してみましょう。
  print "Perl開発環境出来上がり\n";
に変更して実行してみます。

PS E:\TestDir\Perl\sample> cd 'E:\TestDir\Perl\sample/'; ${env:PERLDB_OPTS}='RemotePort=localhost:55237'; & 'perl' '-d' 'E:\TestDir\Perl\sample/first.pl'
Perl髢狗匱迺ー蠅・・縺ァ縺阪≠縺後j

文字化けしますね orz

use utf8;
を追加して実行してみました。

PS E:\TestDir\Perl\sample> cd 'E:\TestDir\Perl\sample/'; ${env:PERLDB_OPTS}='RemotePort=localhost:55237'; & 'perl' '-d' 'E:\TestDir\Perl\sample/first.pl'
Wide character in print at E:\TestDir\Perl\sample/first.pl line 6.
Perl髢狗匱迺ー蠅・・縺ァ縺阪≠縺後j

有名な、Wide character エラーまで出力されるようになってしまいました。

use utf8; の代わりに
use open IO => qw/:encoding(UTF-8) :std/;

を加えてみましょうか・・・
PS E:\TestDir\Perl\sample> cd 'E:\TestDir\Perl\sample/'; ${env:PERLDB_OPTS}='RemotePort=localhost:55242'; & 'perl' '-d' 'E:\TestDir\Perl\sample/first.pl'
Perlテゥツ鳴凝ァツ卍コテァツ陳ーテ・ツ「ツε」ツ・ョテ」ツ・ァテ」ツ・催」ツ・づ」ツ・古」ツつ・

Wide character エラーはなくなりましたが、文字の化け方が変わりました。

こまったぞ。

ふと気づきまして。Visual Studio Code のコンソール出力には PS **** と出てます。
Power Shell なんですね。
んで、こいつが Shift-JIS ベースなのではないだろうかと思ったわけです。

#! /usr/bin/perl
use strict;
use utf8;
binmode STDOUT, ':encoding(cp932)';

print "Perl開発環境出来上がり\n";

exit;

結果
PS E:\TestDir\Perl\sample> cd 'E:\TestDir\Perl\sample/'; ${env:PERLDB_OPTS}='RemotePort=localhost:55287'; & 'perl' '-d' 'E:\TestDir\Perl\sample/first.pl'
Perl開発環境のできあがり

ちゃんと日本語表示されています。
めでたしめでたし。

Linuxサーバー用のプログラムを Windows 上で開発・デバッグしたいと思っているので、環境に応じて binmode 変えなきゃなりませんね。
さて、どーしようか・・・

つづきは、また今度。

2019年7月30日火曜日

VisualStudioで既存のフォルダをプロジェクトに追加する

諸事情で(謎)、VisualStudio 2015 を使い続けてます。
他のプロジェクトをコピーして新しいプロジェクトとして再構築しようとする際にフォルダ配下のファイルをプロジェクトに追加するとフォルダーなしでプロジェクトに追加されてしまいます。

でもって、新規フォルダで同名のフォルダを追加しようとするとエラー。

WEBを検索すると、エクスプローラからソリューションエクスプローラにフォルダをドラッグ&ドロップするとうまくいくとの記述を見ますが、こちらの環境ではうまく動いてくれません。

ということで、以下の手続きでフォルダを追加しました。

1.VisualStudio を終了
2.目的のプロジェクトの .csproj ファイルをエディタで開く
3.以下の記述を追加
<ItemGroup>
<Folder Include="フォルダ名\" />
</ItemGroup&lgt;
4.保存終了して .sln を開く。

無事フォルダが追加されます。
既存ファイルを追加すると、上記の Folder Include は消えてなくなります。

本題に関係ありませんが、エクスプローラがとてもよく落ちてしまうようになって、ストレスまっくすな状態です。
こっちを何とかしたいw

2017年12月21日木曜日

SharpZipLibでZIP書庫ファイルをZIP書庫にするとエラー?

SharpZipLibを使ってファイルをZIP書庫に圧縮するプログラムで、ZIP書庫を対象にするとうまくいっていないことが判明して調査してみました。
通常の疑似コードだとこんな感じ。
string fromFile = @"c:\test.txt"; //圧縮する元のファイル
string toFile = @"c:\test.zip"; //圧縮ファイル名
FastZip.CreateZip(toFile, work_path, false, "test.txt", null);

これで元ファイルがZIP書庫だと
fromFile = @"c:\test.zip";
toFile = @"c:\test.zip";
となってしまい、読み込み元と書き込み先が同じになってしまいます。

当然回避するようにしていました。(以下疑似コード)
if(fromFile == toFile)
{
  toFile=fromFile + ".t";
}
ファイル名の末尾にさらに .t を追加して対応。
圧縮後ファイル名を変更するようにしておりました。

しかーし。実際は「toFile は他のプロセスが使用中」という例外が出ていました。
見た目、同じファイルが残るので気づかなかった(笑

".t"を"_t"とか".zip"とかにしても同様で。
やむなく、一時的なファイル名(時刻と秒を文字列化した一時ファイル名)を作成して処理するように変更しました。

要するに、SharpZipLib 使う際、圧縮先のファイル名には似たような名前が使えない(らしい)ということのようです。

と、いうことで Outlook用の暗号化添付プラグインをバージョンアップしました。
こちらから最新版をダウンロードできます。
https://seasoft.co.jp/products/BizAttacher.html
無料でも使えます。1か月お試し運用もできます!

2017年9月26日火曜日

VisualStudio のインストールドライブを移動したお話。

C:ドライブの容量確保のため、インストール済みの Visual Studio 2015 と Install Shield LE 2015 をD:ドライブに移そうとして、(例によって)ハマってしまったので、記録しておきます。

手順はカンタンのはずでした。
1.Install Shield LE 2015 をアンインストール
2.Visual Studio 2015 をアンインストール
3.Visual Studio 2015 を D:ドライブにインストール
4.Install Shield LE 2015 をD:ドライブにインストール
5.既存のプロジェクトをビルドしてみる。

【事件番号1:インストール先のドライブやフォルダを変更できない】
3.の手順で VisualStudio 2015 のインストーラを起動しても、インストール先フォルダーの変更ができないんです。
[...]ボタンがディゼーブルで押せないんです。
このままだと、再び C:\Program Files (x86) にインストールすることになってしまいます。

いろいろネットで調べてやってみた。
1.インストーラの引数にパス名を指定。
コマンドプロンプトからインストーラを指定して、/CustomInstallPath パラメータでD:ドライブのパス名を指定してみました。
結果:ダメでした。
インストール先は、指定したフォルダになりませんでした。


2.VisualStudio 完全アンインストーラってのがネット上にあったのでダウンロードして実行。
https://github.com/Microsoft/VisualStudioUninstaller/releases
参考URL
https://qiita.com/yizumi1012xxx/items/b13314c12d5dec627ad7
結果:ダメでした。
インストール先を指定できません。

もうちょっと調べてみました。

3.レジストリエディタでパス情報を変更
この方法は危険なので注意して行ってくださいませ。
(1)[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components]を開きます。
(2)[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components]をエクスポートして file1.reg として保存しておきます。
(3)Visual Studio 2015 をインストールします。
(4)[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Components]をエクスポートして file2.reg として保存します。
(5) file1.reg をインポートします。
(6) file2.reg をインポートします。
参考URL
https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/26fb86e2-edc9-40fb-a004-1abbfb93a9e4/visual-studio-community-2015-d?forum=vsgeneralja
結果:うまくいきました。

インストール完了です。実行してみました。

【事件番号2:%CommonDir%\dte80.olb がないなどのエラー】
起動してみると、いくつものファイルがないというエラーが出てしまいました。
ネット上を検索すると、ファイルを探してコピーすればいい、という記事と、 regsrv32.exe で登録すればいいという記事が見られました。
が、CドライブやDドライブを探しても該当ファイルがなかったのでした笑

んで。

1.インストーラを再度起動して「修復」を選択してみました。

無事、動かせるようになりました。
どうやら、最初にインストールしたとき「管理者として実行」をしなかったのが原因のようでした。

スクリーンショット取ってなくてすみません。m(_._)m

2017年8月7日月曜日

VisualStudio2015+InstallShieldLEでコードサイニング証明書

これまでインストーラーを作成して、signtool.exe を起動してコードサイニング証明書を登録してましたが。

超めんどくさい(笑)

どっかにあるはずだ!と思ってググったけど、丁寧な説明を見つけられず。
しかたなく試行錯誤したら、簡単に登録できちゃいました。

メモとして記録しておきます。
1.スタートメニューで MMC コマンドを入力して起動します。






















2.MMCメニュー「ファイル」-「スナップインの追加と削除」を選択






















3.「証明書」をダブルクリック

















4.証明書を選択してコンテキストメニュー「すべてのタスク」-「エクスポート」を選択。















5.エクスポートウィザードで、秘密キーをエクスポートで「Personal Information Exchange-PKCS #12 (.PFX)」を選びます。




















※実はここで「正しくエクスポートされたときは秘密キーを削除する」をチェックしたものだから大変なことにw



6.パスワードを入力し、出力ファイル名を設定すれば完了します。





















7.Install Shield LE での設定は「6 Prepare for Release」-「Releases」タブで、URL、ファイル名、パスワードを入力し、対象ファイルを選びます。















以上の手続きで、インストーラにコードサイニング証明書が追加されました。


めでたしめでたし。

2017年8月1日火曜日

64bit Windows でサブクラスしたウィンドウが落ちる

メールソフト用のプラグインを作ってますが、なんかよく落ちるようになってしまったな、と感じてました。

VisualStudio でデバッグ中に例外が発生する場所が、サブクラス化したWindowプロシージャの部分で、ずいぶん前に書き上げたところだし、修正もかけてないから、さっぱりわかりませんでした。

で、ググってみたところ
https://stackoverflow.com/questions/41741448/random-crashes-on-windows-10-64bit-with-atl-subclassing
こんな情報がありました。

Windows のバグらしいです。
Windows 10 RS2 とかで修正されるらしいです。

じゃぁ、それまで待ちましょう・・・

と、思ってたのですが、プラグインをお使いいただいている方々もクラッシュしてしまってると申し訳ないし。

ということで、上記リンクに書いてある修正をやってみました。

atlstdthunk.h というファイルの25行目くらいにある
#define
USE_ATL_THUNK2 の行をコメントアウトして再コンパイルする
というものです。



実際にやってみようとすると、atlstdthunk.hは読み取り専用でエディタで変更できませんでした。
そこで。
E:\Common\VC14.0\
atlstdthunk.h にファイルをコピーして編集しました。

プラグインとライブラリのすべてのプロジェクト設定で、「プロパティ」を開き
「構成プロパティ」の「すべての構成」で「VC++ディレクトリ」の先頭に E:\Common\VC14.0を追加します。


これでライブラリとプロジェクトを再ビルドしましたところ、どうやら問題が解決したようです。

再現性がランダムのため、解決したかどうかは今後の経過を見なけりゃなりませんけどね。

2017年6月13日火曜日

___iob_func リンクエラー

以前のプロジェクトを、VisualStudio 2015 で再構築すると、
Error LNK2019: unresolved external symbol ___iob_func referenced in function 
というエラーでビルドに失敗しました。

ちょいと調べてみたら VS2015より前のバージョンでは
#define stdin  (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])
が定義されていて

VS2015では#define stdin  (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))
と定義が異なっているためのエラーのようです。

そう、一部のライブラリについて古いバージョンでのビルドを利用してるわけです。
すべてのライブラリを VS2015 で再ビルドすればいいのですが、よそのプロジェクトだったりしてめんどくさい。

___iob_func はないので、つくっちまおう。ということで。
FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

ってのを書いてビルドしたら動作するようになりました。
次にビルドするときのために記録しておこ。

参考
https://stackoverflow.com/questions/30412951/unresolved-external-symbol-imp-fprintf-and-imp-iob-func-sdl2