githubで公開した時の検索時の説明文(ディスクリプション)を用意
前回から引き続きgithub関係について備忘録としてまとめておきたいと思います。
今回は検索時に表示される説明文(ディスクリプション)を用意する方法です。
■githubをpublicで公開した時に気づいたこと
ここ最近、publicで上げたコードが検索に引っかかることがあるのですが、
説明文(ディスクリプション)がデフォルトのままなんですよね。
ディスクリプションとは、googleなどで検索した際に表示される説明文のことです。
自分の場合、公開したコードが検索に引っかかると
こんな感じになってしまいます。
Contribute to Elsammit/Moguratataki development by creating an account on GitHub.
これじゃ何が何だか分からないですよね。。。
ということで、
今回はこちらの説明文を変更するための方法を調べてみましたのでまとめていきたいと思います。
■ディスクリプションを用意する方法
github上でそれらしい内容が見当たらない。。
ということで、他の方のgithubを参考にすることに!!
githubには多くの方がコードを公開しているので、
「github machine learning python」
などと検索すればすぐにgithubで公開されているコードがヒットします。
ヒットした方々のは記載したであろうコメントが掲載。
何が表示されているのだろうと除いたところ、、
Aboutの内容が表示されている様子!!
Aboutはこちらのようにgithubの右上に表示される説明文のこと。
Aboutはgithub内での説明文として用いられるものだと思っていましたが、
ディスクリプションにも用いられているのですね。
stack overflowにも同じような疑問を持っている方がいらっしゃって、
Aboutを編集すればOKって回答になっていますね。
How do you change a repository description on GitHub? - Stack Overflow
さらに色々調べていくと、、
こちらの方のようにAboutにリンクを貼っておくとリンク内の内容がディスクリプションに表示するもよう。
github.com
■試しに対応
ということで、自分もAboutに記載してみることに。
記載した結果はこんな感じ。
github.com
実際に検索にヒットした時の表記への反映には時間がかかるかと思いますので、しばらく待ちです。
githubのREADMEに動画を表示する
今回はgithubのREADMEに動画を埋め込む方法をまとめてみます。
最近githubでアプリを上げているのですがどういう動作なのかを示すのに、
動画で見せるのが結構手っ取り早かったりするんですよね。
そこで、githubのREADMEに動画を埋め込んでいるのですが、備忘録としてまとめておこうと思います。
■簡単に動画を埋め込む方法
まずは簡単にREADMEに動画を埋め込む方法についてです。
それは、、、
github上でREADME編集画面を開き、任意の位置に動画をドラッグ&ドロップする!!
のみです。
github編集画面にてこちらのように埋め込みたい位置にドラッグ&ドロップしますと動画が挿入されます。
後は下部の”Commit changes”をクリックして保存をすれば、
こちらの通り、動画の埋め込みができます。
今回gif動画を埋め込んでいますが、mp4でも埋め込むことが可能です。
■コミットされた動画をREADMEに埋め込み
次にコミット済みの動画をREADMEに埋め込む方法に関してまとめておきます。
(注)こちらの方法はgif動画のみ対応可能なようです。
方法ですが、
まずgithub上でコミットされたgif動画のパスまで移動し、
URLをコピーしておきます。
キャプチャアプリの場合にはこちらになります。
https://github.com/Elsammit/ScreenCapture/blob/master/Sample/AppImage.gif
次に、先ほどと同様にREADMEを開き、
挿入したい位置に、
![gif](コピーしたURL)
を記載します。
先ほどのgif動画のパスを用いると、
![gif](https://github.com/Elsammit/ScreenCapture/blob/master/Sample/AppImage.gif)
となります。
後は、READMEを保存すれば、、
README上に動画が埋め込まれているわけです。
こちらのREADMEで動画埋め込みの方法はこちらのコミットしたgif動画を表示する方法を用いております。
GitHub - Elsammit/ScreenCapture: スクリーンキャプチャ。
こちらの利点ですが、
①ローカルでの編集でも動画の埋め込みが出来る
②動画保存先をコントロールできる
という点かな?と考えております。
①はドラッグ&ドロップの方法ですとgithubのサイト上でしか出来ない方法なので、
ローカルで作業したい人にはこちらの方法がよいのかな?と思います。
②に関してですが、
ドラッグ&ドロップの方法ですと、
自動的に生成されたパスに動画が保存されるようで、
コントロールが出来ないので、保存先を自分でコントロールしたい場合にもこちらの方法かな?
■最後に
今回はgithubのREADMEに動画を埋め込む方法をまとめました。
ドラッグ&ドロップで動画を埋め込むことが出来るので簡単ですね。
他にもコミットしたgif動画をREADMEに埋め込んでみました。
こちらの方法はgif動画しか対応していないので、注意ください。
32bit UEFIブートかつ64bitアーキテクチャにubuntuをインストールするまで
先日部屋掃除をしていたところ、
昔使用していた2in1タブレットPC(Aspire Switch)を発見!!
懐かしくなり起動させてみたところ、OSがWindows8.1。
せっかくなのでwindows10にアップデートしようとしたのですが、なぜかインストール失敗。。
まぁ、windows10がインストールできたとしてもスペック的にカクついたりしてまともに動かなかっただろう。
このまま眠らせておくのももったいないので、、
せっかくなのでubuntuをインストールすることに!!
下記からubuntuをUSBメモリに焼いてインストールすればよいのだと思い気軽な気持ちで始めたのですが、
ちょっと手間取ったところがあったのでこちらに方法をまとめたいと思います。
Ubuntu 22.04 LTS (Jammy Jellyfish)
■手間取った点
どうやらこちらのタブレット、
64bitアーキテクチャだがブートが32bit UEFIブートと特殊な環境。。
このため、64bit版Ubuntuをインストールしようとしてもブートが異なるため、インストーラが動かせない。
こんな環境でUbuntuインストール出来るのか?
と思い、色々調べたところ、こちらの記事を発見!!
インストールするにあたり参考にさせて頂くことにしました!!
blog.goediy.com
blog.goediy.com
■解決策
こちらの記事に記載されている通り、
ブートローダにbootia32.efiをインストールメディアに含めればOKです。
インストールメディアで起動させる際にbootia32.efiでブートするもよう。
bootia32.efiは、インストールメディアの/EFI/boot配下に格納すればOK。
すでに64bit用のefiファイルが格納されているかと思いますが、一緒に格納しておけばOK。
bootia32.efiは下記手順を参考にすれば作成可能です。
Ubuntu on Acer Aspire Switch 10 · GitHub
、、、が、面倒な方はこちらからダウンロードしてもOKです。
linux-asus-t100ta/bootia32.efi at master · jfwells/linux-asus-t100ta · GitHub
■インストール手順
基本的にはこちらの手順を元に実施すればOKなのですが、1点だけ補足。
blog.goediy.com
USBメモリへのインストールメディアコピーですが、
rufus(Rufus - 起動可能なUSBドライブを簡単に作成できます)
を用いるとwindows上のエクスプローラでEFI/boot配下のフォルダが開けるのでbootia32.efiを入れるのがとても簡単でした。
Linux環境でddでUSBメモリに書き込みを行ってしまうとbootia32.efiを書き込むためにマウントしなければならないので、、、
そんな手間がrufusだとかからないので是非。
後、インストール後のUbuntuをブートする際のgrubコマンドですが、
①lsコマンドでどのパーティションが存在するかチェック
②下記コマンドでデバイスとパーティションをset
grub> set root=(hd*,gpt*)
③下記コマンドでパーティション内に何が入っているのかチェック
ls /
すると、間違ったデバイス、パーティションでbootしなくてよく、混乱も少ないかと思います。
合わせて、、、
今回ご紹介した手順ですと、こちらのコマンドでインストールしていますが、
sudo ubiquity -b
普通にインストールを選択してインストールを開始してしまってもよさそうです。
32bit UEFI搭載の2in1にLinuxをぶち込んだお話 | ちゃろのガジェット日記
自分の環境ですと、
grubが上手く動作せずUSBメモリを抜いてしまうと"No Bootable device"と出てしまったのでコマンドで実行しましたが。
javascriptをtype="module"で読み出すとonclickイベントで関数がis not definedになる
先日調査したjsonデータ⇔csvデータ変換やjsonデータ⇔xmlデータ変換や各種ファイルを出力する方法をまとめました。
elsammit-beginnerblg.hatenablog.com
elsammit-beginnerblg.hatenablog.com
どうせなのでこれらのコードをgithub pagesで公開したら後で便利かな?
と思い立ち、Webアプリを作成中!!
そこで、
ファイル分割のためにexport/importを使用したところ、
ちょっとハマったことがあるので備忘録としてまとめたいと思います。
- ■export/importを用いてファイル分割を行ってみる。
- ■htmlでtype="module"でjavascriptファイルを読み出すとonclickイベントで関数がis not defined
- ■is not definedの原因
- ■is not definedの解決方法
- ■最後に
- ■参考
■export/importを用いてファイル分割を行ってみる。
javascriptでは、export/importを用いることで、別ファイルで定義した関数を使用することが出来るようになります。
例えば、hoge1.jsとhoge2.jsを用意し、足し算を行う関数(add)をhoge2.jsで定義し、hoge1.jsで呼び出したいとします。
その場合には、こちらのように記載すればhoge1.jsでhoge2.jsで定義したadd関数をコール出来ます。
【hoge1.js】
import {add} from './hoge2.js' const addhandler = () =>{ alert(add(1,2)); }
【hoge2.js】
export function add(x,y){ return x+y; }
この際、htmlファイルではhoge1.jsを呼び出す必要があるのですが、
こちらのコードの通り、
<script src="hoge1.js" type="module"></script>
type="module"と定義する必要があります。
htmlファイルのコード。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="hoge1.js" type="module"></script> <title>Document</title> </head> <body> hello world </body> </html>
■htmlでtype="module"でjavascriptファイルを読み出すとonclickイベントで関数がis not defined
先ほど記載しました通り、
export/importでファイル分割して別ファイルの関数を呼び出すことができる。
export/importを使用するためには、
<script src="hoge1.js" type="module"></script>
が必要。
となります。
ここで、hoge1.jsとhtmlファイルをこちらのように変更してみます。
import {add} from './hoge2.js' const addhandler = () =>{ alert(add(1,2)); } const ClickFunc = () =>{ alert('Hello World'); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="hoge1.js" type="module"></script> <title>Document</title> </head> <body> hello world <button type="button" onClick="ClickFunc ()">Click!!(xml)</button> </body> </html>
変更点ですが、
buttonタグを追加してボタンクリックした際に、
onClickイベントによりClickFunc ()がコールされるように追加した
のみになります。
ですが、こちらを実行してみると、、、
ClickFunc が
is not defined
と関数が定義されていない旨のエラーメッセージが表示されます。
■is not definedの原因
type="module"にしたことで”モジュールスコープ”になってしまったことで
is not definedと参照出来ない状況になっていたようです。
モジュールスコープになっていることにより、
モジュール内のすべての宣言は、モジュールにスコープされます。
他のjavasciptがこれらの宣言にアクセスしようとすると、参照エラーがスローされます。
これにより、
hoge1.jsで定義されたClickFunc はhoge1.js内でのみでしか使用できない状態のため、
onClickイベントにて関数が見つけられない(is not defined)になってしまった。
ということです。
■is not definedの解決方法
解決方法ですが、hoge1.jsで定義するClickFunc()に対して下記の通り、
明示的にwindowオブジェクトへ公開すればOKです。
import {add} from './hoge2.js' const addhandler = () =>{ alert(add(1,2)); } window.ClickFunc = () =>{ alert('Hello World'); }
もしくは、onClickは用いずに
addEventListenerハンドラーを用いればOKです。
■最後に
今回は自分がはまったtype="module"で、
onclickイベントで関数がis not definedになる現象に関して原因と解決方法をまとめました。
今回は、onclickイベントを例に記載しましたが
onloadやonChangeなどでhtmlから呼び出す関数に対して、
今回の解決方法を適用しないとis not definedになってしまいます。
注意しておかないとハマってしまいそうですね。。
気を付けて実装していきます。
ウィンドウ上の指定した領域に対するスクリーン録画アプリをリリースしました
先日、指定した領域に対してスクリーンショット(録画)をする方法をまとめました。
elsammit-beginnerblg.hatenablog.com
今後、自分でも何かと使いそうだな!!と思ったので、
アプリケーションを作成しました。
■アプリダウンロード先
アプリはgithubにて公開いたしました。
ダウンロード先のURLはこちらになります。
https://github.com/Elsammit/ScreenCapture/archive/refs/tags/Version1.0.zip
また、ソースコードもgithubにて公開しておりますのでこちらもどうぞ!!
GitHub - Elsammit/ScreenCapture: スクリーンキャプチャ。
■使用条件(動作確認環境)
・OS:Windows10 64bit
・メモリ:8GB以上
・ディスプレイ:デュアルディスプレイ以上で使用ください。
※シングルディスプレイですと、
アプリ上に表示される映像が繰り返されてしまい、正しくスクリーンを録画できなくなります。
・ディスプレイ解像度:1920x1080
■アプリ紹介
今回作成したアプリはこちらの通り、
右下に録画用のボタンと中央にスクリーン全体の映像を表示する構成としました。
赤枠が録画を行う領域になります。
こちらはマウスを左クリックしながら動かせば指定することが可能です。
尚、赤枠を指定していない場合にはスクリーン全体を録画する仕様です。
録画開始ボタンを押すと保存先を指定するダイアログが表示され、
ファイルを選択するとこちらの通り、録画画面が表示されます。
。。といっても大きく変わっている点は、
右上にRecと録画時間が表示されるのみなのですが。
また録画指定エリア(赤枠)ですが、録画中は変更できない仕様にしております。
録画を終了する際は右下の録画停止ボタンを押してください。
■アプリを使用する際の注意事項
こちらにアプリを使用する際の注意事項を記載いたします。
【注意時事項】
・デュアルディスプレイ以上で使用している場合、プライマリディスプレイのみが録画対象になります。
・録画ファイルのフォーマットはwmv固定です。
・録画時間は最長30分です(30分を超えると強制的に録画終了します)。
WPFでディスクトップ上の指定したエリアのスクリーン録画を行ってみた
自分でカスタマイズしながらスクリーンショットアプリが欲しくて色々試してきました。
今回はこちらで紹介したウィンドウ全体を表示、録画する方法をまとめました。
elsammit-beginnerblg.hatenablog.com
また、Rctangleで描画する方法もまとめました。
elsammit-beginnerblg.hatenablog.com
今回はこちらのディスクトップウィンドウのスクリーン録画する方法と、
Rectangleを描画する方法を組み合わせて、、、
Rectangleで描画した部分を切り出してスクリーン録画する方法をまとめたいと思います。
これで画面上の好きなエリアのみを録画することが出来るようになります。
■完成形イメージ
アプリの完成形ですが、
こちらのように中央にディスクトップウィンドウを表示させ、
マウスで指定エリア指定した後に録画ボタンを押すと指定したエリアを切り出して録画をする。
といった形になります。
■ソースコードについて
ソースコードはこちらに公開しております。
コード全体を確認したい方はこちらをご参考ください。
github.com
■画面デザイン(xaml)
画面デザインですが、こちらのように作成しました。
CanvasとImageを同じ大きさ位置で表示するように作成し、Imageに対してBorderで枠線を引いております。
Canvas(& Image)のサイズですが、
Height:720px
Width:1280px
としております。
後はボタンが用意されているのみのシンプルな画面になります。
<Window x:Name="ScreenCapture" x:Class="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp1" mc:Ignorable="d" Title="ScreenCaptureツール" Height="792.901" Width="1485.972" Closing="CloseWindow" Loaded="WindowLoad" ResizeMode="NoResize" Icon="DispIcon.jpeg" Background="#FF5D5D5D"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition/> </Grid.ColumnDefinitions> <Canvas x:Name="RectArea" HorizontalAlignment="Left" Margin="17,18,0,0" VerticalAlignment="Top" Height="720" Width="1280" MouseLeftButtonDown="MouseLeftBtnDwn" MouseMove="MouseMoving" MouseLeftButtonUp="MouseLeftBtnUp" Panel.ZIndex="1" Background="Transparent" OpacityMask="Gray"/> <Border BorderBrush="White" BorderThickness="1" HorizontalAlignment="Left" Height="720" Margin="17,18,0,0" VerticalAlignment="Top" Width="1280"> <Image x:Name="ImgCap" HorizontalAlignment="Left" Height="720" VerticalAlignment="Top" Width="1280" Margin="0,0,0,0"/> </Border> <Button x:Name="StartButton" Content="録画開始" HorizontalAlignment="Left" Height="74" Margin="1317,659,0,0" VerticalAlignment="Top" Width="136" Click="Button_Click" RenderTransformOrigin="1.155,1.833" Panel.ZIndex="2" FontSize="24"> <Button.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform AngleX="-2.969"/> <RotateTransform/> <TranslateTransform X="-4.567"/> </TransformGroup> </Button.RenderTransform> </Button> <Border x:Name="RecBlock" BorderBrush="Black" BorderThickness="1" HorizontalAlignment="Left" Height="56" Margin="1332,32,0,0" VerticalAlignment="Top" Width="122" CornerRadius="30" Background="#FFFB4646" RenderTransformOrigin="-6.375,10.312"> <TextBlock TextWrapping="Wrap" Text="REC" Margin="19,11" TextAlignment="Center" Foreground="White" FontSize="24"/> </Border> <Label x:Name="RecTimer" Content="00:00" HorizontalAlignment="Left" Margin="1358,109,0,0" VerticalAlignment="Top" Height="40" Width="76" FontSize="24" Foreground="White"/> </Grid> </Window>
■指定したエリアに限定した録画方法
指定したエリアに限定して録画をするコードはこちらになります。
ここで、screenBmpはウィンドウ全体のBitmapイメージであり、rectが自身で指定したRectangleのエリアになります。
private bool WriteVideo(bool isStartRec, Bitmap screenBmp, RECT rect) { m_recordData = rect; int capWidth = m_recordData.right - m_recordData.left; int capHeight = m_recordData.bottom - m_recordData.top; if (capHeight <= 0 || capWidth <= 0) { Console.WriteLine(" size Error"); return false; } System.Drawing.Rectangle rectBuf = new System.Drawing.Rectangle(rect.left, rect.top, capWidth, capHeight); Bitmap bmp = screenBmp.Clone(rectBuf, screenBmp.PixelFormat); Mat mat = BitmapConverter.ToMat(bmp).CvtColor(ColorConversionCodes.RGB2BGR); if (isStartRec) { Cv2.CvtColor(mat, mat, ColorConversionCodes.BGR2RGB); Cv2.Resize(mat, mat, new OpenCvSharp.Size(capWidth, capHeight)); writer.Write(mat); } return true; }
実施していることですが、
System.Drawing.Rectangle rectBuf = new System.Drawing.Rectangle(rect.left, rect.top,
capWidth, capHeight);
で切り出し領域のRectangleデータを作成し、
Bitmap bmp = screenBmp.Clone(rectBuf, screenBmp.PixelFormat);
にてウィンドウ全体から切り出すために作成したRectangleで切り出しを行い、
最後はMat型の変数に変換しOpenCVのWrite関数でデータ書き込みを実施しております。
ウィンドウの切り出しですが、下記のようなことを実施しているイメージです。
■指定したエリアの切り出し原理
先ほど記載した通り、指定した座標やRectangleのサイズを指定すればBitmapイメージの切り出しが可能です。
では、座標やRectangleのサイズはCanvas上でマウスで指定したエリアを利用すればよいのでしょうか?
答えはNoです。
Canvasのサイズと実際のウィンドウサイズは異なりますので、座標やRectangleのサイズをそのまま利用するとおかしな位置が切り出されることになります。
このため、
Canvasのサイズと実際のウィンドウサイズの比率で座標やRectangleのサイズを変更させる必要があります。
指定エリアの比率変更のコードはこちらになります。
public bool DrawRectangle(System.Windows.Point point, double canvasWidth, double canvasHeight, ref Position position, ref System.Windows.Shapes.Rectangle rectangle) { bool ret = false; rectangle.Stroke = new SolidColorBrush(Colors.Red); rectangle.StrokeThickness = 1; var width = Math.Abs(InitPos.X - point.X); var height = Math.Abs(InitPos.Y - point.Y); rectangle.Width = width; rectangle.Height = height; if (point.X > canvasWidth - 1) { width = canvasWidth - InitPos.X; Canvas.SetLeft(rectangle, InitPos.X); rectangle.Width = width; position.left = (int)(InitPos.X); } else if (point.X < 0) { width = InitPos.X; Canvas.SetLeft(rectangle, 0); rectangle.Width = width; position.left = 0; } else if (InitPos.X < point.X) { Canvas.SetLeft(rectangle, InitPos.X); position.left = (int)(InitPos.X); } else { Canvas.SetLeft(rectangle, point.X); position.left = (int)(point.X); } if (point.Y > canvasHeight - 1) { height = canvasHeight - InitPos.Y; Canvas.SetTop(rectangle, InitPos.Y); rectangle.Height = height; position.top = (int)(InitPos.Y); } else if (point.Y < 0) { height = InitPos.Y; Canvas.SetTop(rectangle, 0); rectangle.Height = height; position.top = 0; } else if (InitPos.Y < point.Y) { Canvas.SetTop(rectangle, InitPos.Y); position.top = (int)(InitPos.Y); } else { Canvas.SetTop(rectangle, point.Y); position.top = (int)(point.Y); } position.width = (int)(width * (SystemParameters.PrimaryScreenWidth / canvasWidth)); position.height = (int)(height * (SystemParameters.PrimaryScreenHeight / canvasHeight)); position.top = (int)(position.top * (SystemParameters.PrimaryScreenHeight / canvasHeight)); position.left = (int)(position.left * (SystemParameters.PrimaryScreenWidth / canvasWidth)); return ret; } }
一部Rectangleの描画が入っておりますが、こちらの解説は
elsammit-beginnerblg.hatenablog.com
を参考にしてください。
比率の変更コードですが、
position.width = (int)(width * (SystemParameters.PrimaryScreenWidth / canvasWidth)); position.height = (int)(height * (SystemParameters.PrimaryScreenHeight / canvasHeight)); position.top = (int)(position.top * (SystemParameters.PrimaryScreenHeight / canvasHeight)); position.left = (int)(position.left * (SystemParameters.PrimaryScreenWidth / canvasWidth));
になります。
実施していることは、
実際のウィンドウとCanvasのサイズの比率を計算の上、比率でCanvas上に作成したRectangleのサイズを拡大・縮小しております。
■最後に
これで自分が録画したいエリアを指定することが出来るようになりました。
後でこちらのアプリはリリースしてみようかと思います。
まずは自分で使ってみて、使い勝手確認していきます。
CreateBitmapSourceFromHBitmapでメモリリークが発生した件
先日よりC#で画面スクリーンショット方法をまとめていました。
elsammit-beginnerblg.hatenablog.com
elsammit-beginnerblg.hatenablog.com
そこで、CreateBitmapSourceFromHBitmap関数でどうもメモリリークが発生してしまい、
メモリ使用量がどんどん増えていく。。
ということで原因と対応策を調べたので備忘録としてまとめておくことしました。
※同じミスはしたくないので。
■原因
メモリリークが発生していたコードですが、こちらのような実装をしておりました。
using (var bmtpScreen = new System.Drawing.Bitmap( (int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { using (var bmpFromImg = Graphics.FromImage(bmtpScreen)) { bmpFromImg.CopyFromScreen(0, 0, 0, 0, bmtpScreen.Size); Imaging.CreateBitmapSourceFromHBitmap( bmtpScreen.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); } }
こちらのコードですと、
CreateBitmapSourceFromHBitmap実行を繰り返す度にメモリリークが発生してしまい、
困ったことになりました。
そもそもCreateBitmapSourceFromHBitmap関数の引数ですが、
・第1引数:アンマネージ ビットマップへのポインター(IntPtr)
・第2引数:ビットマップのパレットマップへのポインター(IntPtr)
・第3引数:ソースイメージのサイズ(Int32Rect)
・第4引数:変換を処理する方法を指定する列挙体の値(BitmapSizeOptions)
となります。
※参考:https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.interop.imaging.createbitmapsourcefromhbitmap?view=windowsdesktop-6.0
自分で作成したコードですが、
return Imaging.CreateBitmapSourceFromHBitmap(
bmtpScreen.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
でしたので、第1引数のbmtpScreen.GetHbitmap()が怪しそう。。
ということで調べてみたところ、
第1引数に与えている bmtpScreen.GetHbitmap()は明示的に開放しないとメモリリークにつながることを発見。
fkmt5.hatenadiary.org
自分のコードだとメモリ開放はしていないので、
メモリが開放されず蓄積してしまいメモリリークにつながってしまっていたようです。
■解決方法
bmtpScreen.GetHbitmap()のメモリを解放すればよいことが分かりましたので、実際に開放処理を追加すればOK。
開放にはDeleteObject()を利用することにしました。
DeleteObject()とは、、、
WIn32 APIの1つで、
論理ペン、ブラシ、フォント、ビットマップ、領域、またはパレットを削除し、オブジェクトに関連付けられているすべてのシステムリソースを解放
する関数になります。
オブジェクトが削除されると、指定されたハンドルは無効になります。
※参考:https://docs.microsoft.com/ja-jp/windows/win32/api/wingdi/nf-wingdi-deleteobject
DeleteObject()を利用する場合にはあらかじめ、
[System.Runtime.InteropServices.DllImport("gdi32.dll")] public static extern bool DeleteObject(IntPtr hObject);
と定義すればOK。
後は、
using (var bmtpScreen = new System.Drawing.Bitmap( (int)SystemParameters.PrimaryScreenWidth, (int)SystemParameters.PrimaryScreenHeight, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { using (var bmpFromImg = Graphics.FromImage(bmtpScreen)) { bmpFromImg.CopyFromScreen(0, 0, 0, 0, bmtpScreen.Size); var ptrBuf = bmtpScreen.GetHbitmap(); Imaging.CreateBitmapSourceFromHBitmap( ptrBuf , IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); DeleteObject(ptrBuf); } }
といったように、
DeleteObjectで、
var ptrBuf = bmtpScreen.GetHbitmap()
で定義したリソースを明示的に開放してあげればOKです。
■最後に
今回は自分が躓いたメモリリークに関してまとめておきました。
次回同じようなコードを書く時には気を付けたいと思います。