DOBON.NET DOBON.NETプログラミング掲示板過去ログ

WPF ImageコントロールのhDCを取得したい

環境/言語:[VB.NET 2010]
分類:[.NET]

こんにちは。
WPFで、デバイスコンテキストを取得したくて苦労しています。

既存のライブラリが、hDCを渡したらそこに描画してくれるもので
VB6、VB.NET WinFormなどでの動作は確認できたのですが
WPFになって、Imageコントロールへ描画したいと思っていますが
hDCが取得できずに困っています。

もしかして、hDCは取得できないものなのでしょうか。

また、参考になるソースコードのリンクなどでもいいので

ご存知の方、教えていただければと思います。

WPF HDCなどで検索したのですが、

System.Drawing.Bitmapが出てくるサンプルがみつかるものの
それを、どのようにImageコントロールへ
結びつけてよいものかがわかりません。

ImageコントロールにSourceプロパティで画像ファイルを指定して
画像を描画していますが、この画像のhDCが取得できるといいなと
思っております。

WinFormでいうところの
PictureBox1.CreateGraphics.GetHdc
がWPFのイメージコントロールでやりたいのです。

よろしくお願いします。
System.Windows.Interop.WindowInteropHelper でググるといいかも
HWND→HDC
WindowsFormsHostでWinFormのコントロールをホストする、のが一番楽ですかね。
■No29199に返信(Hongliangさんの記事)

> System.Windows.Interop.WindowInteropHelper でググるといいかも
> HWND→HDC

ImageコントロールのHWNDを求めて、hDCを求める…のですか?
ググっても情報が少ないので、難しいですな。

> WindowsFormsHostでWinFormのコントロールをホストする、のが一番楽ですかね。

それはできているのですが、そうじゃない方法で作っていきたいのです。

Imageコントロール以外に
System.Drawing.Bitmapを作ってそれをImage1.Sourceに割り当てるのは
下記のようにできました。

ImageコントロールそのものからhDCは取得できないのかな?

  Dim bmp As New System.Drawing.Bitmap(200, 200)
  bmp =
    DirectCast(
     System.Drawing.Image.FromFile("C:\Windows\Globalization\MCT\MCT-JP\Wallpaper\JP-wp6.jpg"),
     System.Drawing.Bitmap)

  Dim r As Int32Rect
  r.X = 0
  r.Y = 0
  r.Width = 100
  r.Height = 100

  Image1.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap, IntPtr.Zero,
   r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)
  '-----


  Dim g = System.Drawing.Graphics.FromImage(bmp)
  Dim hdc As IntPtr = g.GetHdc
> ImageコントロールそのものからhDCは取得できないのかな?
はい、できません。
jijiさん、 Hongliangさん、お返事ありがとうございます。

少しわかってきましたが、難しいです。

hDCを渡して描画を行うテストとして
StretchBltを使ってhDCを渡してみましたが
Image1に設定した画像からImage2に描かれてくれないです。

描画対象と表示させているものが、何かが違うような気がしていますが
何が生成されているのか、何に描画しているのかが
まだよくわかっていません。

このあたりお詳しい方、教えてください。

Image2に描画というよりかは、Bitmapに描画できてそれをImage2に表示できれば
それでよいのですが
もしかして、WPFではこのような描画ができないということなのでしょうか。

下記のコードで
Image1には画像が表示されます。
Image2に少し拡大した画像が表示されてほしいですが
何も描画されません。

Imports System.Runtime.InteropServices


Const SRCCOPY As Integer = 13369376

<DllImport("gdi32.dll")> _
Private Shared Function StretchBlt(ByVal hDestDC As IntPtr, _
    ByVal nLeftDest As Integer, ByVal nTopDest As Integer, _
    ByVal nWidthDest As Integer, ByVal nHeightDest As Integer, _
    ByVal hDCSrc As IntPtr, _
    ByVal nLeftSrc As Integer, ByVal nTopSrc As Integer, _
    ByVal nWidthSrc As Integer, ByVal nHeightSrc As Integer, _
    ByVal dwRop As Integer) As Integer
End Function

Sub TestStretchBlt(ByVal hDCSrc As Integer, ByVal hDCDest As Integer)
    StretchBlt(hDCDest, 0, 0, 100, 100, hDCSrc, 0, 0, 50, 50, SRCCOPY)
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click

    Dim r As Int32Rect
    r.X = 0
    r.Y = 0
    r.Width = 100
    r.Height = 100

    Dim bmp1 As New System.Drawing.Bitmap(200, 200)
    bmp1 =
        DirectCast(
          System.Drawing.Image.FromFile("C:\Windows\Globalization\MCT\MCT-JP\Wallpaper\JP-wp6.jpg"), 
          System.Drawing.Bitmap)

    Image1.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp1.GetHbitmap, IntPtr.Zero,
      r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)

    Dim hdc1 As IntPtr = System.Drawing.Graphics.FromImage(bmp1).GetHdc()

    Dim bmp2 As New System.Drawing.Bitmap(200, 200)
    Dim hdc2 As IntPtr = System.Drawing.Graphics.FromImage(bmp2).GetHdc

    TestStretchBlt(hdc1, hdc2)

    Image2.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp2.GetHbitmap, IntPtr.Zero,
      r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)

End Sub
> StretchBltを使ってhDCを渡してみましたが
> Image1に設定した画像からImage2に描かれてくれないです。
たしか FromImage で作成した Graphics の GetHdc で返される DC であっても、SelectObject する必要があったような。

■No29200 で書かれてるような記述で十分だと思いますよ。
// GetHbitmap で取得した HBITMAP は DeleteObject が必要ですが。
■No29206に返信(Hongliangさんの記事)
>>StretchBltを使ってhDCを渡してみましたが
>>Image1に設定した画像からImage2に描かれてくれないです。
> たしか FromImage で作成した Graphics の GetHdc で返される DC であっても、SelectObject する必要があったような。
> 
> ■No29200 で書かれてるような記述で十分だと思いますよ。
> // GetHbitmap で取得した HBITMAP は DeleteObject が必要ですが。

んー、やはり動かないです。
すいません、描画処理についてだいぶわかってないのですが

追加
<DllImport("gdi32.dll")>
Private Shared Function SelectObject(ByVal hdc As Integer, ByVal hObject As Integer) As Integer
End Function

略

    Image1.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp1.GetHbitmap, IntPtr.Zero,
      r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)

    Dim g1 As System.Drawing.Graphics
    g1 = System.Drawing.Graphics.FromImage(bmp1)
    Dim hdc1 As IntPtr = g1.GetHdc()
    SelectObject(hdc1, bmp1.GetHbitmap)

    Dim bmp2 As New System.Drawing.Bitmap(200, 200)
    Dim g2 As System.Drawing.Graphics
    g2 = System.Drawing.Graphics.FromImage(bmp2)
    Dim hdc2 As IntPtr = g2.GetHdc
    SelectObject(hdc2, bmp2.GetHbitmap)

    TestStretchBlt(hdc1, hdc2)

    Image2.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp2.GetHbitmap, IntPtr.Zero,
      r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)

このようにSelectObjectを使ってみましたが、やはりImage2には何も描画されないです。

こういうことを言っておられるのですか?
GetHbitmap() は新しい HBITMAP を作成して返すので、毎度毎度呼び出していてはいけませんね。
■No29208に返信(Hongliangさんの記事)
> GetHbitmap() は新しい HBITMAP を作成して返すので、毎度毎度呼び出していてはいけませんね。

では、どのように記述するといいのでしょうか?
対象のBitmapのインスタンスを作成した後にGetHbitmapした戻り値をIntPtr型の変数に格納しておけばいいのではないでしょうか。
え、いや、普通に変数に確保して……。
サンプルを改良させていただいて
次のように描画したら、
デスクトップキャプチャ画像が拡大された大きさで描かれるので
StretchBltは動作しているので、WPFでも
描画処理はうまくいくみたいです。

Public Shared Function CaptureScreen() As Bitmap
    'プライマリモニタのデバイスコンテキストを取得
    Dim disDC As IntPtr = GetDC(IntPtr.Zero)
    'Bitmapの作成
    Dim bmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, _
        Screen.PrimaryScreen.Bounds.Height)
    'Graphicsの作成
    Dim g As Graphics = Graphics.FromImage(bmp)
    'Graphicsのデバイスコンテキストを取得
    Dim hDC As IntPtr = g.GetHdc()
    'Bitmapに画像をコピーする

'    BitBlt(hDC, 0, 0, bmp.Width, bmp.Height, disDC, 0, 0, SRCCOPY)
    TestStretchBlt(disDC, hDC)

    '解放
    g.ReleaseHdc(hDC)
    g.Dispose()
    ReleaseDC(IntPtr.Zero, disDC)

    Return bmp
End Function


Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click

    Dim CaputurBmp As Bitmap = CaptureScreen()

    Dim r As Int32Rect
    r.X = 0
    r.Y = 0
    r.Width = 100
    r.Height = 100

    Image3.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(CaputurBmp.GetHbitmap, IntPtr.Zero,
      r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)

End Sub


だけど、Bitmapオブジェクトに対するStretchBltが下記のコードで動かないです。

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
    Dim r As Int32Rect
    r.X = 0
    r.Y = 0
    r.Width = 100
    r.Height = 100
    Dim fnt As New Font("MS UI Gothic", 20)

    Dim bmp1 As New Bitmap(200, 200)
    bmp1 =
        DirectCast(
          Image.FromFile("C:\Windows\Globalization\MCT\MCT-JP\Wallpaper\JP-wp6.jpg"), 
          Bitmap)

    Dim g1 As Graphics
    g1 = Graphics.FromImage(bmp1)
    g1.DrawString("TEST1", fnt, Brushes.Red, 0, 0)

    Dim bmp2 As New Bitmap(200, 200)
    Dim g2 As Graphics
    g2 = Graphics.FromImage(bmp2)

    Dim hdc2 As IntPtr = g2.GetHdc()
    Dim hdc1 As IntPtr = g1.GetHdc()

    TestStretchBlt(hdc1, hdc2)

    g1.ReleaseHdc()
    g2.ReleaseHdc()

    g2.DrawString("TEST2", fnt, Brushes.Green, 0, 50)

    g1.Dispose()
    g2.Dispose()

    Image1.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp1.GetHbitmap, IntPtr.Zero,
      r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)

    Image2.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bmp2.GetHbitmap, IntPtr.Zero,
      r, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions)

End Sub

DrawStringの内容は描画されるけど
bmp1にうつっている画像が
StretchBltで、bmp2に映ってほしいだけなんですが…

なんか
PictureBoxのImageプロパティに関するよくある勘違い: .NET Tips: C#, VB.NET, Visual Studio
http://dobon.net/vb/dotnet/graphics/pictureboximageanddrawimage.html
ここで示されている罠に陥っている気がしますが、
何が悪いのか、さっぱりわかりません。

困ったな…
できました!

StretchBltの前にSelectObjectが必要だったようです。

SelectObject(hdc1, bmp1.GetHbitmap)
TestStretchBlt(hdc1, hdc2)

ありがとうございました。
解決済みを押すのを忘れていました。

■No29213に返信(FutoNekoさんの記事)
> できました!
>
> StretchBltの前にSelectObjectが必要だったようです。
>
> SelectObject(hdc1, bmp1.GetHbitmap)
> TestStretchBlt(hdc1, hdc2)
>
> ありがとうございました。
>
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板