« 2013年09月 | メイン | 2013年11月 »

2013年10月 アーカイブ

2013年10月03日

[C#][.NET] .NET アプリケーション (WPF/Windows フォーム) で多重起動を禁止し、単一のプロセスで動作させる

■ 概要

.NET アプリケーション (WPF/Windows フォーム) で多重起動を禁止し、単一のプロセスで動作するようにする方法を C# で示す。

■ 解説

WPF アプリケーションや Windows フォーム アプリケーションにおいて、多重起動を禁止してみよう。

ここでは、既にアプリケーションが起動していているときに、そのアプリケーションでドキュメントを開こうとした場合 (*1) に、

  • 他のプロセスが起動していなければ、普通にドキュメントを開く。
  • 他のプロセスが既に起動していれば、その起動中のアプリケーションでドキュメントを開く。

という動作を実現する。

(*1) アプリケーションでドキュメントを開くのには、次のようなケースが考えられる。

  • 実行ファイルに、ドキュメント ファイルをドラッグ&ドロップ
    例. 実行ファイルを a.exe とすると、ファイル エクスプローラーで a.png を a.exe にドラッグ&ドロップ
  • コマンド プロンプトで、コマンドライン引数にドキュメント ファイルを指定して実行ファイルを起動
    例. 実行ファイルのパスを c:\demo\a.exe、ドキュメント ファイル c:\demo\a.png とすると、コマンド プロンプトを開き、c:\demo\a.exe c:\demo\a.png[Enter] と入力
  • ファイルをアプリケーションに関連付けし、開く。

■ 実現方法

すでに起動しているかのチェック方法

ここでは Mutex を用いる。詳細はサンプル コードを参照のこと。

すでに起動しているアプリケーションとの通信方法

ここでは Ipc を用いる。詳細はサンプル コードを参照のこと。

■ サンプル コード

WPF と Windows フォームの両方のサンプルを示そうと思う。

■ 共通部 (WPF、Windows フォーム非依存)

先ずは共通部から。

SingleDocument.cs
// 多重起動を禁止する
// - 他のプロセスが起動していなければ、普通にドキュメントを開く。
// - 他のプロセスが既に起動していれば、その起動中のアプリケーションでドキュメントを開く。
// WPF、Windows フォーム非依存
// 参照設定 System.Runtime.Remoting が必要

using System;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using System.Threading;

namespace SingleDocumentSample
{
    // .NET で多重起動しないアプリケーションを作成するためのクラス (WPF、Windows フォーム非依存)
    public class SingleDocumentHelper
    {
        // 複数のプロセスで一つしかないことを保証する
        class Singleton : IDisposable
        {
            readonly Mutex mutex = null; // 複数のプロセスから参照するために Mutex を使用

            public bool IsExist // 既に存在しているかどうか
            {
                get
                {
                    return !mutex.WaitOne(0, false); // Mutex が既に作成されていたら true
                }
            }

            public Singleton(string uniqueName)
            {
                mutex = new Mutex(false, uniqueName); // コンストラクターで、このアプリケーション独自の名前で Mutex を作成
            }

            public void Dispose()
            { mutex.Dispose(); }
        }

        // Ipc によって複数のプロセスで共有するアイテムを格納する
        class IpcRemoteObject : MarshalByRefObject
        {
            public object Item { get; set; }

            // IpcRemoteObject に複数のプロセスで共有するアイテムを格納
            public static void Send(string channelName, string itemName, object item)
            {
                var channel = new IpcClientChannel(); // IPC (プロセス間通信) クライアント チャンネルの生成
                ChannelServices.RegisterChannel(channel, true); // チャンネルを登録
                var remoteObject = Activator.GetObject(typeof(IpcRemoteObject), string.Format("ipc://{0}/{1}", channelName, itemName)) as IpcRemoteObject; // リモートオブジェクトを取得
                if (remoteObject != null)
                    remoteObject.Item = item;
            }
        }

        // 指定された時間毎に Tick イベントが起きるオブジェクトのインタフェイス
        // アプリケーション側で実装する
        public interface ITicker : IDisposable
        {
            event Action Tick;

            void Start();
        }

        // 指定された時間毎に IpcRemoteObject をチェックし、空でなければ Receive イベントが起きる
        class Receiver : IDisposable
        {
            public event Action<object> Receive;

            IpcRemoteObject remoteObject;
            readonly ITicker ticker;

            public Receiver(string uniqueName, string itemName, ITicker ticker)
            {
                Initialize(uniqueName, itemName);
                this.ticker = ticker;
                ticker.Tick += OnTick;
                ticker.Start();
            }

            public void Dispose()
            { ticker.Dispose(); }

            void Initialize(string uniqueName, string itemName)
            {
                var channel = new IpcServerChannel(uniqueName); // このアプリケーション独自の名前で IPC (プロセス間通信) サーバー チャンネルを作成
                ChannelServices.RegisterChannel(channel, true); // チャンネルを登録
                remoteObject = new IpcRemoteObject(); // リモートオブジェクトを生成
                RemotingServices.Marshal(remoteObject, itemName, typeof(IpcRemoteObject)); // リモートオブジェクトを公開
            }

            void OnTick()
            {
                if (remoteObject.Item != null) { // リモートオブジェクトがあれば
                    if (Receive != null)
                        Receive(remoteObject.Item); // Receive イベントを起こす
                    remoteObject.Item = null;
                }
            }
        }

        // 多重起動しないアプリケーション (他のプロセスが起動済みの場合は、その起動済みのプロセスにドキュメントを開かせ、終了する)
        public class Application : IDisposable
        {
            const string itemName = "DocumentPathName";

            Singleton singleton = null;
            Receiver receiver = null;

            // 初期化 (戻り値が偽の場合は終了)
            public bool Initialize(ITicker ticker, string documentPath = "", Action<string> open = null)
            {
                var applicationProductName = ((AssemblyProductAttribute)Attribute.GetCustomAttribute(Assembly.GetExecutingAssembly(), typeof(AssemblyProductAttribute))).Product;

                singleton = new Singleton(applicationProductName);
                if (singleton.IsExist) { // 既に他のプロセスが起動していたら
                    IpcRemoteObject.Send(applicationProductName, itemName, documentPath); // その起動済みのプロセスにドキュメントのパスを送信して
                    return false; // 終了する
                }
                receiver = new Receiver(applicationProductName, itemName, ticker);
                if (open != null) {
                    receiver.Receive += item => open((string)item); // Receive イベントが起きたらアイテムを open するように設定
                    if (!string.IsNullOrWhiteSpace(documentPath)) // documentPath が空でなければ
                        open(documentPath); // documentPath を開く
                }
                return true; // 終了しない
            }

            public void Dispose()
            {
                if (receiver != null) {
                    receiver.Dispose();
                    receiver = null;
                }
                if (singleton != null) {
                    singleton.Dispose();
                    singleton = null;
                }
            }
        }
    }
}
■ WPF の場合

上記 SingleDocumentHelper クラスを WPF で使う場合は次のようになる。

SingleDocumentWpfApplication.cs

App.xaml.cs のアプリケーション クラスのベース クラス。

タイマーに System.Windows.Threading.DispatcherTimer クラスを使う。

using System;
using System.Windows;
using System.Windows.Threading;

namespace SingleDocumentSample.Wpf
{
    // WPF 多重起動防止アプリケーション
    public class SingleDocumentApplication : Application
    {
        // WPF 用の ITicker の実装
        // 指定された時間毎に Tick イベントが起きる
        class Ticker : SingleDocumentHelper.ITicker
        {
            public event Action Tick;

            public const int DefaultInterval = 1000;
            readonly DispatcherTimer timer;

            public Ticker(int interval = DefaultInterval)
            { timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(interval) }; }

            public void Start()
            {
                // DispatcherTimer を使用して定期的に Tick イベントを起こす
                timer.Tick += (sender, e) => RaiseTick();
                timer.Start();
            }

            public void Dispose()
            { timer.Stop(); }

            void RaiseTick()
            {
                if (Tick != null)
                    Tick();
            }
        }

        public event Action<string> OpenDocument;

        readonly SingleDocumentHelper.Application singleDocumentApplication = new SingleDocumentHelper.Application();
        string documentPath = null;

        protected string DocumentPath
        {
            get { return documentPath; }
            set {
                documentPath = value;
                if (OpenDocument != null)
                    OpenDocument(value); // ドキュメントを開く
            }
        }

        public SingleDocumentApplication(Window mainWindow)
        { MainWindow = mainWindow; }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            if (e.Args.Length > 0) // コマンドライン引数があれば
                DocumentPath = e.Args[0]; // ドキュメント ファイルのパスとする

            if (singleDocumentApplication.Initialize(new Ticker(), DocumentPath, sourcePath => DocumentPath = sourcePath))
                MainWindow.Show();
            else
                Shutdown(); // 他のプロセスが既に起動していれば終了
        }

        protected override void OnExit(ExitEventArgs e)
        {
            singleDocumentApplication.Dispose();
            base.OnExit(e);
        }
    }
}
App.xaml
<l:SingleDocumentApplication x:Class="SingleDocumentSample.Wpf.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:SingleDocumentSample.Wpf" />
App.xaml.cs

上記 SingleDocumentApplication クラスから派生。

using System.Windows;

namespace SingleDocumentSample.Wpf
{
    partial class App : SingleDocumentApplication
    {
        public App() : base(new MainWindow())
        { OpenDocument += sourcePath => ((MainWindow)MainWindow).DocumentPath = sourcePath; }
    }
}
MainWindow.xaml

WebBrowser コントロールが貼ってあるだけ。

<Window x:Class="SingleDocumentSample.Wpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="SingleDocumentSample.Wpf" Height="500" Width="500">
    <Grid>
        <WebBrowser x:Name="browser" />
    </Grid>
</Window>
MainWindow.xaml.cs

指定されたパスや URL を WebBrowser コントロールで表示する。

using System;
using System.Windows;

namespace SingleDocumentSample.Wpf
{
    partial class MainWindow : Window
    {
        public string DocumentPath
        {
            get { return browser.Source == null ? null : browser.Source.AbsolutePath; }
            set {
                browser.Source = new Uri(value, UriKind.RelativeOrAbsolute);
                Title          = value;
                ToTop();
            }
        }

        public MainWindow()
        { InitializeComponent(); }

        // ウィンドウを最前面に表示
        void ToTop()
        {
            if (WindowState == WindowState.Minimized)
                WindowState = WindowState.Normal;
            Activate();
        }
    }
}
■ Windows フォームの場合

そして、SingleDocumentHelper クラスを Windows フォームで使う場合は次のようになる。

SingleDocumentWindowsFormApplication.cs

Main メソッドで使用するためのクラス。

タイマーに System.Windows.Forms.Timer クラスを使う。

using System;
using System.Windows.Forms;

namespace SingleDocumentSample.WinForm
{
    class SingleDocumentApplication
    {
        // ウィンドウズ フォーム用の ITicker の実装
        // 指定された時間毎に Tick イベントが起きる
        class Ticker : SingleDocumentHelper.ITicker
        {
            public event Action Tick;

            public const int DefaultInterval = 1000;
            readonly Timer timer;

            public Ticker(int interval = DefaultInterval)
            { timer = new Timer { Interval = interval }; }

            public void Start()
            {
                // Timer を使用して定期的に Tick イベントを起こす
                timer.Tick += (sender, e) => RaiseTick();
                timer.Start();
            }

            public void Dispose()
            { timer.Dispose(); }

            void RaiseTick()
            {
                if (Tick != null)
                    Tick();
            }
        }

        public event Action<string> OpenDocument;

        string documentPath = null;

        protected string DocumentPath
        {
            get { return documentPath; }
            set {
                documentPath = value;
                if (OpenDocument != null)
                    OpenDocument(value); // ドキュメントを開く
            }
        }

        public bool Run(Form mainForm)
        {
            var commandLineArgs = Environment.GetCommandLineArgs();
            if (commandLineArgs.Length > 1) // コマンドライン引数があれば
                DocumentPath = commandLineArgs[1]; // ドキュメント ファイルのパスとする

            using (var singleDocumentApplication = new SingleDocumentHelper.Application()) {
                if (singleDocumentApplication.Initialize(new Ticker(), DocumentPath, sourcePath => DocumentPath = sourcePath)) {
                    Application.Run(mainForm);
                    return true;
                }
                return false; // 他のプロセスが既に起動していれば終了
            }
        }
    }
}
Program.cs

Main メソッド。

using System;

namespace SingleDocumentSample.WinForm
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var application = new SingleDocumentApplication();
            var mainForm    = new MainForm();
            application.OpenDocument += documentPath => mainForm.DocumentPath = documentPath;
            application.Run(mainForm);
        }
    }
}
MainForm.cs (MainForm.Designer.cs は不要)

WebBrowser コントロールが貼ってある。

指定されたパスや URL を WebBrowser コントロールで表示する。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace SingleDocumentSample.WinForm
{
    class MainForm : Form
    {
        readonly WebBrowser browser;

        public string DocumentPath
        {
            set {
                browser.Navigate(new Uri(value, UriKind.RelativeOrAbsolute));
                Text = value;
                ToTop();
            }
        }

        public MainForm()
        {
            SuspendLayout();
            ClientSize = new Size(500, 500);
            Text       = "SingleDocumentSampleWinForm";
            browser    = new WebBrowser { Dock = DockStyle.Fill };
            Controls.Add(browser);
            ResumeLayout(false);
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
                browser.Dispose();
            base.Dispose(disposing);
        }

        // ウィンドウを最前面に表示
        void ToTop()
        {
            if (WindowState == FormWindowState.Minimized)
                WindowState = FormWindowState.Normal;
            Activate();
        }
    }
}

■ 実行方法

  1. WPF 版または Windows フォーム版をビルドする。
  2. ファイル エクスプローラーで実行ファイル (例えば、SingleDocumentSample.Wpf.exe や SingleDocumentSample.WinForm.exe) のあるフォルダーを開き、実行ファイルのアイコンに画像ファイル (JPEG ファイルや PNG ファイル) 等をドラッグ&ドロップする。 (または、コマンドプロンプトで実行ファイルのバスにコマンドライン引数で画像ファイルのパスやURLを渡す)
  3. サンプル プログラムが起動し、その画像ファイルが表示される。
  4. サンプル プログラムを最小化し、再度ファイル エクスプローラーで別の画像ファイルをドラッグ&ドロップする。 (または、コマンドプロンプトで実行ファイルのバスにコマンドライン引数で別の画像ファイルのパスやURLを渡す)
  5. 今度はプログラムは起動せず、最小化されていた先程のサンプル プログラムが最前面に戻り、新たな画像ファイルが表示される。
実行イメージ
実行イメージ

2013年10月30日

[C] CP/M80 シミュレーターで懐かしの BDS-C を動かしてみた

BDS C プログラミング

先日、自宅の本棚を見ていたら、ちょっと古めのIT関連本が有ったので、写真を撮って SNS にアップしてみたところ、IT系の知人が結構懐かしがった。

「ちょっと古めのIT本」
「ちょっと古めのIT本」

Z80MC6809アセンブリ言語の話題から、BDS-C の話題になった。

私も色々思い出してきて、どんどん懐かしくなってきた。

BDS-C について

BDS-C は、BD Software 製の i8080/Z80 用のプログラミング言語 Cコンパイラーだ。CP/M80 上で使えた。

C のサブセットで、浮動小数点数を直接サポートしておらず、代わりに浮動小数点演算ライブラリを使うようになっていた。廉価版に α-C があり、当時の C の処理系としては安かった (はっきりとは覚えていないが、他が10万円クラスだったのが、α-C は数万円程度だった気がする)。

BDS-C を試してみよう

懐かしくなったので、BDS-C を動かしてみることにした。ちなみに、手元の環境は、Windows 8.1。

BDS-C は現在無償で公開されている。

このサイトで、BDS-C のコンパイラー (cc.com、cc2.com) やリンカー (clink.com)、そして、それらの CP/M80 用のアセンブリ言語のソースコード、ユーザー マニュアル等をダウンロードすることができる。

CP/M80 は手元にないので、シミュレーターを使うことにした。

SIMH Altair 8800 simulator

シミュレーターと CP/M、BDSーC のディスク イメージのダウンロード

CP/M のシミュレーター/エミュレーターは色々とあるようだが、Altair 8800 のシミュレーター SIMH Altair 8800 simulator を使うことにした。

Altair 8800 は、1974年に開発された世界初の個人向けコンピューターで、これ用の BASIC インタープリタービル・ゲイツポール・アレンが書いたのがマイクロソフトの始まりになったことで有名だ。

サイトではシミュレーター本体だけでなく、CP/M80 を含んだ BDS-C のディスク イメージもダウンロードすることができる。

尚、このサイトでは、他にもこのシミュレーター用の沢山のプログラミング言語の処理系や OS、アプリケーション、ゲーム等のディスク イメージをダウンロードできる。

シミュレーターと CP/M、BDSーC の起動

ダウンロードした二つの圧縮ファイル (altairz80.zip と bdsc.zip) を解凍して、一つのフォルダーに入れる。

ここでは、「C:\Altair 8800 simulator」というフォルダーを作成した。

中身は、次のようになった (コマンド プロンプト)。

Microsoft Windows [Version 6.3.9600]
(c) 2013 Microsoft Corporation. All rights reserved.

C:\>cd C:\Altair 8800 simulator

C:\Altair 8800 simulator>dir /b
altairz80.exe
altairz80_doc.pdf
bdsC
bdsc160.dsk
bdsc160source.dsk
cpm2.dsk
i.dsk
simh_doc.pdf
simh_faq.pdf

C:\Altair 8800 simulator>

シミュレーターを起動し、CP/M 付き BDS-C のディスク イメージを読み込み、CP/M を立ち上げてみよう。

コマンド プロンプトで、「altairz80 bdsC」と入力する。

画面は次のようになる (コマンド プロンプトの続き)。

C:\Altair 8800 simulator>altairz80 bdsC

Altair 8800 (Z80) simulator V3.9-0 build 1625 (scp created Feb 10 2013 at 09:37:
45 with gcc 4.2.4)


64K CP/M Version 2.2 (SIMH ALTAIR 8800, BIOS V1.23, 2 HD, 08-Oct-06)

A>
---------------
Altair 8800 (Z80) simulator V3.9-0 build 1625 (scp created Feb 10 2013 at 09:37:
45 with gcc 4.2.4)


64K CP/M Version 2.2 (SIMH ALTAIR 8800, BIOS V1.23, 2 HD, 08-Oct-06)

通常のドライブ A の他に、BDS-C のドライブが二つ (B ドライブと C ドライブ) 追加されている。C ドライブには、CC.ASM 等 BDS-C 自体のソースコードが入っているようだ。

試しに、それぞれのドライブの中を見てみよう。

A>dir
A: PIP      COM : LS       COM : XSUB     COM : STAT     COM
A: GO       COM : RSETSIMH MAC : SYSCOPY  COM : XFORMAT  COM
A: SPEED    COM : W        COM : L80      COM : M80      COM
A: WM       HLP : WM       COM : SUBMIT   COM : FORMAT   COM
A: SYSCPM2  SUB : SYSCPM2Z SUB : DDTZ     COM : DSKBOOT  MAC
A: EX       MAC : EX8080   COM : ED       COM : DDT      COM
A: LOAD     COM : ASM      COM : LU       COM : MBASIC   COM
A: ELIZA    BAS : DUMP     COM : CREF80   COM : EXZ80DOC COM
A: EXZ80ALL COM : EX       SUB : UNERA    COM : BOOT     COM
A: OTHELLO  COM : WORM     COM : LADDER   DAT : LADDER   COM
A: ZSID     COM : ZTRAN4   COM : SURVEY   MAC : SHOWSEC  COM
A: BOOT     MAC : HDSKBOOT MAC : TIMER    MAC : UNERA    MAC
A: R        COM : CFGCCP   LIB : CFGCCPZ  LIB : MOVER    MAC
A: CCP      MAC : CCPZ     MAC : USQ      COM : MC       SUB
A: MCC      SUB : MCCL     SUB : RSETSIMH COM : CPU      MAC
A: TIMER    COM : UNCR     COM : SURVEY   COM : CPU      COM
A: COPY     COM : SID      COM : LIB80    COM : CCPZ     TXT
A: ZAP      COM : PRELIM   MAC : PRELIM   COM : BDOS     MAC
A: CBIOSX   MAC : DIF      COM : DO       COM : BOOTGEN  COM
A>dir b:
B: BDSCPAT  Z80 : -READ    ME  : BDS      LIB : CMODEM   C
B: C        CCC : C        SUB : CASM     C   : CASM     SUB
B: CC       COM : CC2      COM : CCC      ASM : CCONFIG  C
B: CCONFIG  COM : CCONFIG  H   : CCONFIG2 C   : CHARIO   C
B: CLIB     COM : CLINK    COM : CLOAD    C   : CRCKLST1 CRC
B: DEFF     CRL : DEFF2    CRL : DEFF2A   CSM : DEFF2B   CSM
B: DEFF2C   CSM : FILES    DOC : L2       C   : SOURCES  LBR
B: STDIO    H   : STDLIB1  C   : STDLIB2  C   : STDLIB3  C
B: ZCASM    LBR : -LBR     NOT : BCD      LBR : BDSCIO   H
B: BUGS     DOC : CDEBUG   LBR : CRCK     COM : CRCK     DOC
B: CRCKLST2 CRC : DEFF15   CRL : LBREXT   COM : LDIR     COM
B: LONG     C   : MCONFIG  H   : RED      LBR : TARGET   C
B: UNCRUNCH COM : CASM     CRL : CASM     COM : CHARIO   CRL
B: L2       COM : CCV16PAT HEX : CLOAD    COM : TAIL     C
B: CCV20PAT HEX : CMODEM   H   : CMODEM2  C   : CP       C
B: DATE     C   : DIO      C   : DIO      H   : HARDWARE H
B: LPR      C   : NDI      C   : NOBOOT   C   : RM       C
B: UCASE    C   : WILDEXP  C
A>dir c:
C: A        SUB : CC       ASM : CC       SUB : CC2      ASM
C: CC2A     ASM : CC2B     ASM : CC2C     ASM : CC2D     ASM
C: CC2E     ASM : CC2L     ASM : CC2SLR   ASM : CCA      ASM
C: CCB      ASM : CCC      ASM : CCD      ASM : CCL      ASM
C: CLINK    ASM : CLINKA   ASM : CLINKB   ASM : CLINKL   ASM
C: LASM     COM : NCC      SUB : NCC2     SUB : NCC2S    SUB
C: NCC2Z    SUB : NCCD     SUB : NCCZ     SUB : NCLINK   SUB
C: NZCLINK  SUB : PUT      BTM : README   TXT : CHARIO   C
C: L2       C   : STDIO    H   : CC       COM : C        CCC
C: CLINK    COM : DEFF     CRL : CC2      COM : DEFF2    CRL
C: L2       COM
A>

B ドライブに、BDS-C のコンパイラー (CC.COM、CC2.COM) やリンカー (CLINK.COM) がある。ちなみに、A ドライブには、BDS-C で書かれたオセロゲーム (OTHELLO.COM) が入っている。

BDSーC で "Hello world!"

では、お約束の "Hello world!" をやってみよう。

CP/M で B ドライブに移動して、ライン エディターの「ed」 (CP/M が内蔵している テキスト エディターの一種) を立ち上げ、hello.c というファイル名で Hello world! と出力する C のプログラムを書く。

勿論、古い C の文法で書く必要がある。

A>b:
B>ed hello.c

NEW FILE
     : *i
    1:  #include <stdio.h>
    2:
    3:  main()
    4:  {
    5:      puts("Hello world!");
    6:  }
    7:
     : *e

B>

hello.c の中身を覗いてみる。書いた通りのソースコードになっているのが確認できる。

B>type hello.c
#include <stdio.h>

main()
{
    puts("Hello world!");
}

B>

では、BDS-C でコンパイルし、リンクしてみよう。コンパイルは cc、リンクは clink だ。

B>cc hello.c
BD Software C Compiler v1.60  (part I)
  36K elbowroom
BD Software C Compiler v1.60 (part II)
  33K to spare
B>clink hello
BD Software C Linker   v1.60

Last code address: 0901
Externals start at 0902, occupy 0006 bytes, last byte at 0907
Top of memory: E405
Stack space: DAFE
Writing output...
  46K link space remaining
B>

エラー無くコンパイルとリンクができたようだ。

実行ファイルが出来たことを確認しよう。

B>dir hello.*
B: HELLO    C   : HELLO    CRL : HELLO    COM
B>

問題ないようだ。

では、愈々実行だ。

B>hello
Hello world!
B>

無事 Hello world! と表示された。

めでたし、めでたし。

About 2013年10月

2013年10月にブログ「プログラミング C# - 翔ソフトウェア (Sho's)」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2013年09月です。

次のアーカイブは2013年11月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Powered by
Movable Type 3.35