PowerShellでアプリケーションアイコンを作ってみる2。csproj作成

powershell コンピュータ
powershell

PowerShellでアイコンを作ることが出来ましたので、アイコンを組み込んだプロジェクトのようなものを作成してみます。

<#
.SYNOPSIS
 .csproj新規作成

.EXAMPLE
 New-Csproj -ProjectName プロジェクト名 [-Target:winexe|exe] [-Outdir:"."]

#>

Add-Type -AssemblyName System
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing


Set-StrictMode -Version Latest
$ErrorActionPreference = "STOP"


function CreateResx
{
    param(
        [string]
        $IconPath,
        [string]
        $OutPath
    )

    $fs = [System.IO.FileStream]::new($IconPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
    $Icon = [System.Drawing.Icon]::new($fs)
    $fs.Close()
    $resx = [System.Resources.ResXResourceWriter]::new($OutPath) | % {
            $_.AddResource("APP_ICON",$Icon)
            $_
    }
    $resx.Close()
    $Icon.Dispose()
}

function CreateTextIcon
{
    param(
        [string]
        $Text,
        [string]
        $OutPath,
        [int]
        $W = 64,
        [int]
        $H = 64
    )
        
    $Bitmap = [System.Drawing.Bitmap]::new(64, 64, [System.Drawing.Imaging.PixelFormat]::Format24bppRgb)
    $g = [System.Drawing.Graphics]::FromImage($Bitmap)
    # 黒で塗りつぶし
    $rect = [System.Drawing.Rectangle]::new(0, 0, $W, $H)
    $g.FillRectangle([System.Drawing.Brushes]::Black, $rect);
    # 文字を描く
    $font = [System.Drawing.Font]::new("MS UI Gothic", 32);

    $g.DrawString($Text.Substring(0, 2), $font, [System.Drawing.Brushes]::White, 0, 0);
    $g.Dispose()

    $icon = [System.Drawing.Icon]::FromHandle($Bitmap.GetHicon())
    $fs = [System.IO.FileStream]::new($OutPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
    $icon.Save($fs)
    $fs.Close()
    $icon.Dispose()
    $Bitmap.Dispose()

}

function New-Csproj
{
    param(
        [Parameter(ValueFromPipeline=$false,Mandatory=$true)]
        [string]
        $ProjectName,
        [Parameter(ValueFromPipeline=$false,Mandatory=$false)]
        [string]
        $Target = "winexe",
        [Parameter(ValueFromPipeline=$false,Mandatory=$false)]
        [string]
        $Outdir = (Resolve-Path(".")).Path

    )

$cspoj_temp = @'
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>  
   <AssemblyName></AssemblyName>
    <OutputPath>Bin\</OutputPath>
    <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
  </PropertyGroup>  
  <ItemGroup>
    <Compile Include="*.cs"/>
    <Reference Include="System.Windows.Forms.dll" />
    <Reference Include="System.Drawing.dll" />
    <Reference Include="System.IO.Compression.dll" />
    <Reference Include="System.IO.Compression.FileSystem.dll" />
  </ItemGroup>
  <Target Name="Build" Inputs="@(Compile)" Outputs="$(OutputPath)$(AssemblyName).exe">
    <MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
    <Csc Sources="@(Compile)" TargetType=""  References="@(Reference)" Resources="@(Resource)" OutputAssembly="$(OutputPath)$(AssemblyName).exe"/>
  </Target>
  <Target Name="Clean" >  
    <Delete Files="$(OutputPath)$(AssemblyName).exe" />
  </Target>
  <Target Name="Rebuild" DependsOnTargets="Clean;Build" />
  <Target Name="Run" DependsOnTargets="Rebuild">
    <Exec Command="$(OutputPath)$(AssemblyName).exe" />
  </Target>
</Project>
'@

# アプリケーションのソースファイルを作成
$consol_app_temp = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ${projectname}
{
    class Program
    {
        // アプリケーションのエントリポイント
        static void Main(string[] args)
        {
            Console.Write("${projectname}");
        }
    }
}
"@

$form_app_temp = @"
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

using System.Text.RegularExpressions;
using System.IO;
using System.IO.Compression;
using System.Resources;


namespace ${projectname}
{
    class Form1 : Form
    {
        public Form1()
        {
            var asm = this.GetType().Assembly;
            using (var s = asm.GetManifestResourceStream("${projectname}.resx"))
            using (var r = new ResXResourceSet(s))
            {
                this.Icon = r.GetObject("APP_ICON") as Icon;
            }


            this.Load += Form1_Load;
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            this.Text = "${projectname}";
        }
        [STAThread]
        static void Main(string[] args)
        {
            Application.Run(new Form1());
        }
    }
}
"@


    # プロジェクト用ディレクトリの作成
    $projdir = Join-Path $Outdir $ProjectName
    if (-not(Test-Path($projdir))) {
        mkdir $projdir | Out-Null
    }


    # csprojの作成
    $local = Join-Path $projdir ("{0}.csproj" -f $ProjectName)
    $xml = [xml]$cspoj_temp

    $xml.Project.PropertyGroup.AssemblyName = $projectname
    $xml.Project.Target | ? {$_.Name -eq "Build"} | % {
        $_.Csc.TargetType = $Target
    }

    $source_path = Join-Path $projdir ("{0}.cs" -f $projectname)

    if ($Target -eq "exe") {
        $source = $consol_app_temp
        $source | %{[Text.Encoding]::UTF8.GetBytes($_)} | Set-Content $source_path -Encoding Byte
    }
    if ($Target -eq "winexe") {
        $source_path = Join-Path $projdir "From1.cs"
        $source = $form_app_temp
        $source | %{[Text.Encoding]::UTF8.GetBytes($_)} | Set-Content $source_path -Encoding Byte

        # アプリケーションアイコンの作成
        $iconPath = Join-Path $projdir "${ProjectName}.ico"
        CreateTextIcon $ProjectName $iconPath

        # resxの作成
        $resxPath = (Join-Path $projdir "${ProjectName}.resx")
        CreateResx $iconPath $resxPath

        #<Resource Include="*.resx" />
        $child = $xml.CreateElement("Resource", $xml.Project.xmlns)
        $attr = $xml.CreateAttribute("Include")
        $attr.Value = "*.resx"
        $child.Attributes.Append($attr)
        $xml.Project.ItemGroup.AppendChild($child) | Out-Null

        #<Csc Sources="@(Compile)" TargetType=""  References="@(Reference)" Resources="@(Resource)" Win32Icon="$(AssemblyName).ico" OutputAssembly="$(OutputPath)$(AssemblyName).exe"/>
        $attr = $xml.CreateAttribute("Win32Icon")
        $attr.Value = '$(AssemblyName).ico'
        $xml.Project.Target | ? {$_.Name -eq "Build"} | % {
            $_.Csc.Attributes.Append($attr)
        }
    }

    $xml.Save($local)

}

Export-ModuleMember -Function New-Csproj

PowerShellのモジュールですので、c:/Users/<ユーザー名>/Documents/WindowsPowerShell/Modules/New-Csproj/New-Csproj.psm1というファイル名で保存してください。(途中のディレクトリが無い場合はディレクトリを作成)

使い方
New-CSproj <プロジェクト名>
実行するとプロジェクト名のディレクトリが出来上がりますので、そのディレクトリに移動します。
cd <プロジェクト名>
ビルドコマンドを実行します。
msbuild /t:run
フォームウィンドウ上部のタイトルバーの左側にアイコンが割り当てられています。

また、タスクトレイにもアイコンが表示されます。

コメント