어찌보면 단순한 이야기일수도 있겠지만, 실제로 하려다보니 여러가지 문제가 생겨서 
해결과정을 글로 남겨두려고 한다.

하려고 하는것은 WebPage에서 로컬PC 내부에 저장되어있는 exe 파일을 실행하는것이다.

처음 시작하기전에 생각한것은 아래와같다.

1. ActiveX를 사용해서 로컬PC의 파일을 다이렉트로 실행한다. 
2. 레지스트 등록을 통해서 정해진 키값을 WebPage내부에서 Script 호출해서 사용한다. 
3. 브라우져에서 파일접근 확인창이 매번 뜨지않도록 제어한다.(Chrome, IE, Edge)


시작전에 생각한 문제점
1. 인터넷 브라우저에 특정되지 않는가? (IE 외에는 ActiveX 실행이 어렵지 않을까?)
2. 레지스트 등록은 관리자 권한이 없으면 안된다.


천리길도 한걸음부터….
우선, 하나하나 시작해보도록 한다.

1. ActiveX를 사용해서 로컬PC의 파일을 다이렉트로 실행한다.

<script LANGUAGE="JavaScript">
function cmd() {
    var obj = new ActiveXObject("WScript.Shell");
    obj.Run("C:\\test\\sample.exe");
}
</script>
<body>
---ActiveX Sample---<br><br>
<a href="javascript:cmd()">ActiveX sample</a><br>
</body>

단순히 위와같은 코드로 효과를 볼수가있다.
실행하면 아래와같이 된다.

아래부분의 하이퍼링크를 클릭하면 sample.exe 가 실행된다.
하지만, 예외없이 경고창이 등장한다. (필자가 일본에 있어서, 일본어판 윈도우를 사용하고 있습니다.)

이 페이지의 ACtiveX 컨트롤은 안전하지않을 가능성이 있고, 페이지 외의 부분에서 영향을 끼칠수 있습니다. 다른 부분의 영향에 대해 문제가 없습니까?

라고 나오는데, 뭐 실행을 시켜야하니까 그냥 Y 로 속행~~ 하면, 실행이 된다.

다음편에서는 

레지스트 등록을 통해서 정해진 키값을 WebPage내부에서 Script 호출해서 사용한다. 

에 대해서 다루어 보도록하겠다.

WPF프로젝트를 지난 5년간 진행하면서 덩치가 너무 커진탓인지
속도가 너무 늦다는 지적을 현장에서 받아오면서, 이 문제를 어떻게 처리할까... 한참을 고민하던끝에 다음달 릴리즈를 앞둔 마당에 이 글을 적어봅니다.

초기 표시 (렌더링) 속도개선방법
여기에서는 초기 표시, 즉 화면을 인스턴스화해서 그려 질 때까지의 시간을 단축하기위한 개선책과
화면을 다시그릴때의 처리시간 단축방법을 이야기 하고자 합니다.

1. 컨트롤의 재배치 처리횟수를 줄이자
  WPF 는 기본적으로 화면의 각 요소는 상대적 좌표 크기를 결정합니다.
  컨트롤의 "HorizontalAlignment"또는 "Width"에 "Auto"를 설정할 수있는 것이 그 증거입니다.
  이렇게함으로써 사용자가 화면 크기를 변경하거나 해상도가 다른 디스플레이에서도
  화면의 표시내용을 담아내는것을 제작하는 것이 가능하게되어 있습니다.

  그러나 이때의 배치 구조는 Xaml 부모 요소에서 자식 요소에 여러 번 왕래하고 크기를 조정하고
  최종 렌더링된다는 것입니다.
  이 재배치 처리에 시간이 걸리면 당연히 표시 할 때까지의 시간도 늦어집니다.

  이 문제를 해결하려면 다음 방법을 검토하십시오.

크기를 고정한다.
  ⇒Width / Height는 고정값으로 합니다.
부모 패널을 Canvas 한다.
  ⇒ 절대 좌표로 지정하여 그리기 좌표를 재계산하지 않아도됩니다.
문자 혹은 도형을 코드로 렌더링한다.
  ⇒ Xaml 대신 코드 측에서 직접 그리는 방법입니다.
   자세한 내용은 코드로 문자 나 도형을 그리기를 참조하십시오.

2. 자신 컨트롤을 UserControl에서 사용자 지정 컨트롤로 변경한다.
  WPF 에서 컨트롤을 만드는 방법은 주로 다음 두 가지입니다.
  (1) UserControl에서 컨트롤을 복합적으로 사용한다.
  (2) 사용자 지정 컨트롤 모양과 구조를 직접 구현한다.
    (또는 기존의 컨트롤을 상속 기능을 추가한다.)

  기본적인것이지만, 컨트롤 클래스는 파생 할 정도로 기능이 증가 · 복잡화하고
  재배치 처리에 시간이 걸릴 수 있습니다.
  UserControl은 Window와 마찬가지로 View를 가지고 있기 때문에 재배치 처리에 시간이 걸립니다. 
  UserControl의 수가 증가할수록 내부의 요소가 복잡할수록 그것은 눈에 띄게됩니다.

  이를 방지하려면 사용자 정의 컨트롤을 구현하십시오.
  사용자 정의 컨트롤을 구현하는 프로젝트는 Visual Studio 에 포함되어 있습니다.
  (프로젝트의 추가 메뉴에서 " WPF 사용자 정의 컨트롤 라이브러리"라는 항목이 있습니다.)
  일반 프로젝트에 사용자 지정 컨트롤을 추가하지 않도록 주의합시다.

3. 의존관계 속성을 정확히 사용한다.
  의존관계 속성은 데이터 바인딩을 사용하는 구조에서
  사용자 컨트롤의 속성은 기본적으로 이를 구현합니다.
  일반적 속성도 처음에만 데이터 바인딩은 작동하지만,
  이후의 속성 값의 변화에 대응할 수 없습니다.
  또한 일반적인 속성은 런타임에 데이터 바인딩을 사용할 때,
  리플렉션을 이용하고 있기 때문에 의존관계 속성보다 속도가 느립니다.

4. 렌더링 속도를 향상시킬 수있는 속성
  사용자 컨트롤의 기본 클래스 "Control"또는 "FrameworkElement"에는
  값을 설정하여 렌더링 속도를 향상시킬 수있는 몇 가지 속성이 존재합니다.
  Xaml 의 상대적인 레이아웃 설정의 장점을 없애는 것도 있지만,
  다음의 속성을 설정하는 것을 고려하십시오.

· IsHitTestVisible (기본값 : false)
   true로 설정하면 컨트롤을 수행 할 수 없습니다.
   마우스 오버와 클릭 등의 작업을 일체행하지 않습니다.
   조작이 불필요한 컨트롤에 이것을 설정하여 불필요한 이벤트가 발생하지 않고,
   속도의 향상을 기대할 수 있습니다.

· ClipToBounds (기본값 : false)
   true로 설정하면 컨트롤의 자식 요소가 자신의 그리기 범위를 벗어난 영역에 그려지지 않습니다.
   그리기 영역이 한정되기 때문에, 동작 속도의 향상을 기대할 수 있습니다.
   그러나 재배치 작업이 발생하는 점에 주의하십시오.

· Width / Height (기본값 : Auto)
   고정 값으로 설정하면 재배치 처리 횟수를 줄일 수 있습니다.
   마찬가지로 "MaxWidth", "MinWidth", "MaxHeight", "MinHeight"도 고정 값으로 설정하는 것을 권장합니다.

UserControl 전반적으로 위의 문제를 다시한번 생각하고 재배치하여, 
기존 3초정도의 렌더링완료가 되었던 화면이 1초만에 완료되도록 개선되었기에 
정보를 정리해두도록 합니다.

파일을 Drag&Drop해서 프리뷰 하는 방법에 대해서 간략하게 적어본다.

일단, 아래의 링크에 접속해서  파일을 다운로드 받아야한다.

http://www.adobe.com/devnet/acrobat/sdk/eula.html

자신의 환경에 맞는 파일을 다운받도록 합니다.

<소스관련>
화면구성은 아래와 같습니다. (일본에서 개발중이라, 일본어가 섞여 있습니다.)

메인화면 디자인

화면을 구성한 컨트롤은 아래와 같다.

리스트박스 1
PrintPreview 1
PictureBox 1
printDocument 1


실행하면, 아래와 같은 동작을 합니다.

<실행화면>

리스트박스에 엑셀파일, PDF 파일을 드래그 앤 드롭 실시

파일 드래그 앤 드롭
드래그 앤 드롭 후
PDF파일 더블클릭

물론, PDF 파일이기때문에 양쪽에 표시된 화살표를 클릭해서 추가 기능을 열고 접을수 있습니다.



코드는 아래와 같습니다.

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

namespace DragAndDrop
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //ListBox1のDragEnterイベントハンドラ
        private void ListBox1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
        {
            //コントロール内にドラッグされたとき実行される
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                //ドラッグされたデータ形式を調べ、ファイルのときはコピーとする
                e.Effect = DragDropEffects.Copy;
            else
                //ファイル以外は受け付けない
                e.Effect = DragDropEffects.None;
        }

        //ListBox1のDragDropイベントハンドラ
        private void ListBox1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
        {
            //コントロール内にドロップされたとき実行される
            //ドロップされたすべてのファイル名を取得する
            string[] fileName = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            //ListBoxに追加する
            listBox1.Items.AddRange(fileName);
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedItem != null)
            {
                DialogResult dr = MessageBox.Show("指定したファイルを削除しますか?", "【ファイル削除】", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning);

                if (dr == DialogResult.OK)
                {
                    System.IO.File.Delete(listBox1.SelectedItem.ToString());
                    listBox1.Items.Remove(listBox1.SelectedItem);
                }
            }
            else
            {
                MessageBox.Show("選択されたアイテムがありません。", "【ファイル削除】", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void listBox1_DoubleClick(object sender, EventArgs e)
        {
            // 選択したファイルが存在する場合
            if (listBox1.SelectedItem != null)
            {
                // 拡張子を取得
                string extensionKey = System.IO.Path.GetExtension(listBox1.SelectedItem.ToString()).ToLower();

                switch (extensionKey)
                {
                    case ".pdf":     // PDFファイル
                        // ブラウザコントロールの作成
                        axAcroPDF1.Visible = true;
                        printPreviewControl1.Visible = false;

                        axAcroPDF1.LoadFile(listBox1.SelectedItem.ToString());
                        break;

                    case ".csv":     // CSVファイル
                        axAcroPDF1.Visible = false;
                        printPreviewControl1.Visible = true;

                        printPreviewControl1.Document = printDocument1;

                        break;

                    case ".xlsx":    // EXCELファイル
                        axAcroPDF1.Visible = false;
                        printPreviewControl1.Visible = true;

                        break;

                    default:
                        axAcroPDF1.Visible = false;
                        printPreviewControl1.Visible = false;

                        /* 特定できてない拡張子が来た場合を想定 */
                        break;
                }
            }
        }

        private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
        {
            //画像を読み込む
            Image img = Image.FromFile(listBox1.SelectedItem.ToString());
            //画像を描画する
            e.Graphics.DrawImage(img, 0, 0, img.Width, img.Height);
            //次のページがないことを通知する
            e.HasMorePages = false;
            //後始末をする
            img.Dispose();
        }
    }
}


소스에서 xlsx(엑셀) csv에 대해서는 아직 구현하지 않았고 보류중인 기능입니다.
참고로, 아래의 링크에 현재 구성된 소스 

사용된 소스 전체는 아래의 링크에서 다운받을수 있습니다.
아래의 계정을 github에서 follow 해두시면, 수정 및 업데이트 내용을 알람으로 받으실수 있습니다.

https://github.com/sungmanko/DragAndDrop

 

sungmanko/DragAndDrop

DragAndDrop Sample. Contribute to sungmanko/DragAndDrop development by creating an account on GitHub.

github.com

 

추가로 궁금하신점이 있으시면 아래의 링크를 통해서도 보다 많은 정보를 얻으실수 있습니다.
도움되시길 바랍니다.

<C#가이드>
https://docs.microsoft.com/ko-kr/dotnet/csharp/?WT.mc_id=DT-MVP-4039890

<C# 8.0의 새로운 기능>
https://docs.microsoft.com/ko-kr/dotnet/csharp/whats-new/csharp-8?WT.mc_id=DT-MVP-4039890

<VB가이드>
https://docs.microsoft.com/ko-kr/dotnet/visual-basic/?WT.mc_id=DT-MVP-4039890

<VB새로운기능>
https://docs.microsoft.com/ko-kr/dotnet/visual-basic/getting-started/whats-new/?WT.mc_id=DT-MVP-4039890

 


질문
C# TabControl TabPage안의 TextBox 생성

C# 버튼을 누르면 TabControl의 TabPage가 생성하는건 됬는데요...


string title = "TabPage " + (tabControl1.TabCount + 1).ToString(); 
TabPage myTabPage = new TabPage(title); 
tabControl1.TabPages.Add(myTabPage); 


TabPage가 생성하면서 그안에 TextBox도 함께 만들어져야 하는데... 어떻게 만들어야할까요?

그리고 그 TextBox의 크기와 Name은 어떻게 지어야 할까요?


================================================================================


버튼을 클릭하면 자동으로 탭을 생성하면서,

텍스트박스도 생성하고

텍스트박스는 크기를 지정하고, 이름도 지정하게 한다.


아래와같은 코드로 처리가 가능하다.

1
2
3
4
5
6
7
8
9
10
            string title = "TabPage " + (tabControl1.TabCount + 1).ToString();
            TabPage myTabPage = new TabPage(title);
            TextBox tb = new TextBox();
 
            tb.Name = "TextBox" + (tabControl1.TabCount + 1).ToString();
            tb.Width = 250;
            tb.Text = "TextBox" + (tabControl1.TabCount + 1).ToString();
 
            myTabPage.Controls.Add(tb);
            tabControl1.TabPages.Add(myTabPage);
cs


tb.Name 에서 컨트롤의 이름을 지정하고

tb.Width 에서 컨트롤의 크기를 지정하고

tb.Text 에서 초기에 텍스트박스에 보여줄 내용을 지정한다.


결과는 아래와같다.


샘플소스는 아래에서 다운받을수 있습니다.

TabControlAdd.zip


ShowDialog로 호출되던 폼을 Show로 바꾸면서 화면비표시를 하고싶다.


프로젝트는 C#으로 이루어져 있으며, VB6.0과 연동되어 실행되고 있었다.

구성은 아래와 같다.


문제는 해당폼이 Modal 기동된다는 점이다.
Modal 띄워놓고, 화면은 보여주면 안된다.
이것을 어떻게 해결할것인가…
나는 이번 수정을 두가지 방법으로 생각해보았다.
1. 해당폼을 생성하지않고, Load / Shown 이벤트의 내용을 그대로 메소드1에서 수행한다.
2. 해당폼을 보여주지않도록 수정하고, 그냥 진행한다.

일단, 비동기 형식으로 접근을 생각해보았다.
솔류션을 새로생성하고,폼을 두개가 되도록 배치한다.

그리고, 폼1은 다음과같이 디자인하였다.

결국 버튼2만 사용하여 해결하였지만,
여기서 중요한것은 폼의 객체는 생성하지만, 유져에게 보여주어서는 안된다는점.
그리고 Load / Shown 이벤트는 그대로 진행되어야 한다는점.
두가지의 미션이 가장 중요한 부분이다.
해당 이벤트가 불리어지는지는 어떻게 확인할까?
아래와 같이 확인해보았다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Windows.Forms;
 
namespace ApcEventControl
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();
        }
 
        private void Form2_Load(object sender, EventArgs e)
        {
            MessageBox.Show("Form2_Load");
        }
 
        private void Form2_FormClosing(object sender, FormClosingEventArgs e)
        {
            MessageBox.Show("Form2_FormClosing");
        }
 
        private void Form2_Shown(object sender, EventArgs e)
        {
            MessageBox.Show("Form2_Shown");
        }
 
        private void Form2_FormClosed(object sender, FormClosedEventArgs e)
        {
            MessageBox.Show("Form2_FormClosed");
        }
    }
}
 
cs

폼2에 각각의 이벤트를 생성해서, 메세지박스로 표시
가장 단순하면서, 가장 알아보기 쉬운방법이다.
뭐, 로그로 표현할수도 있었지만, 이번에는 그냥 메세지박스로 표시하기로 한다.

폼1은 아래와같이 구현하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace ApcEventControl
{
    public partial class Form1 : Form
    {
        Form2 fm2;
 
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            fm2 = new Form2();
            fm2.WindowState = FormWindowState.Minimized;
            fm2.ShowDialog();
 
            fm2.ShowInTaskbar = false;
 
            fm2.Hide();
        }
 
        private int XXX()
        {
            fm2 = new Form2();
            fm2.FormClosed += Fm2_FormClosed;
            fm2.WindowState = FormWindowState.Minimized;
            fm2.ShowInTaskbar = false;
            fm2.Show();
 
            var task = Task.Run(() =>
            {
                while (fm2 != null)
                {
                    System.Threading.Thread.Sleep(100);
                }
 
                string a = "";
 
                MessageBox.Show("1");
                MessageBox.Show("2");
                MessageBox.Show("3");
                
                if (a == string.Empty)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            });
 
            return 9;
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            MessageBox.Show(XXX().ToString());
        }
        
        private void Fm2_FormClosed(object sender, FormClosedEventArgs e)
        {
            fm2 = null;
        }
 
        private void button3_Click(object sender, EventArgs e)
        {
            fm2 = new Form2();
            fm2.WindowState = FormWindowState.Minimized;
            fm2.Hide();
            fm2.ShowInTaskbar = false;
            fm2.Show();
 
            MessageBox.Show("1");
            MessageBox.Show("2");
            MessageBox.Show("3");
        }
        
        private void button4_Click(object sender, EventArgs e)
        {
            fm2.Close();
        }
    }
}
 
cs

버튼2에서의 처리프로세스
즉, XXX 라고 지칭해둔 함수를 통한 작업이 수행된다.
여기서 중요한것은 Form2 의 인스턴스를 Form1에 private 선언해두고, 해당 인스턴스를 감시한다는점이다.

1
2
            fm2 = new Form2();
            fm2.FormClosed += Fm2_FormClosed;
cs

formClosed 이벤트를 확장해두었다.


1
2
3
4
        private void Fm2_FormClosed(object sender, FormClosedEventArgs e)
        {
            fm2 = null;
        }
cs

내부처리는 위와같다.
form2의 private 객체를 null로 변환한다.


XXX 메소드에서 Task 를 발행해서, 해당객체가 null이 될때까지 감시하도록 하였으니,
이 객체값으로 null이 지정되는 순간, 해당 루프처리까지 탈출하면서 후반부의 처리가 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
            var task = Task.Run(() =>
            {
                while (fm2 != null)
                {
                    System.Threading.Thread.Sleep(100);
                }
 
                string a = "";
 
                MessageBox.Show("1");
                MessageBox.Show("2");
                MessageBox.Show("3");
                
                if (a == string.Empty)
                {
                    return 0;
                }
                else
                {
                    return 1;
                }
            });
cs

위의소스에서 while 부분이 탈출된다는점이다.
그리고 메세지박스로 1, 메세지박스로 2, 메세지박스로 3 이 순서대로 출력되고,
마지막으로 리턴값이 0으로 돌아오게된다.


버튼4에서 fm2 를 종료하는 처리를 하게해 두었으니, 실행순서는 아래와같이 되겠다.

1. 샘플 프로그램을 실행한다.

2. 버튼2를 클릭한다.

Form2_Load가 출력

Form2_Shown가 출력

9가출력 <- Task.Run의 결과는 돌아오지 않았으므로, 후속처리는 그대로 실행되었기에 9가 돌아온다.

그리고, 여기서 버튼4를 누른순간, 아래와 같은 결과가 출력된다.

Form2_FormClosing가 출력

Form2_FormClosed가 출력

1 이 출력

2 가 출력

3 이 출력

- 끝 -


위의 소스를 이용해서 ShowDialog 로 되어있던 폼을 Show 로 불러오면서, 해당결과를 그대로 얻기위한 방법을 연구해보았다.

혹시나 같은 문제에 봉착한 사람들이 있다면, 조그마한 도움이라도 되고자 이 글을 남겨둔다.


소스는 아래의 링크를 참고하세요.

https://github.com/sungmanko/ApcEventControl

await _context.Movie .FirstOrDefaultAsync(m => m.Id == id); 이 statement 설명좀
await sync 이런것들 마이크로소프트에서 읽어봤는데 한단어 한단어 연결해서 이해가 안되네요

await 은 이 statement가 끝날때 까지 기다리고 context는 데이터베이스에서 
firstordefaultAynch는 뭐하는지 모르겠어요
그리고 m=>램다 표현은 어떤식으로 쓰는지 m은 그냥 쓴건지 ..


========================================================================

_context.Movie .FirstOrDefaultAsync(m => m.Id == id)

_context.Movie
해당 객체에서 id 가 일치하는 하나의 데이터를 찾아낼때까지 혹은 데이터가 전체에 존재하지 않는다는 결과가 나올때까지(객체에 저장된 데이터 전체를 조회) 비동기처리로 진행합니다.

m 은 그냥 Movie 객체라서 m 이라고 해둔건데, 다른 영어로 대체하셔도 상관없습니다.
v 나 z 나 상관없습니다.




사운드 컨트롤을 제어해서 지정된 어플에서만 음성을 받아오도록 해야하는 과제가 주어졌다.

일단, 대전제로 아래와같이 Mute 는 사용하지 않으면서, 해당어플에서 내보내는 경고음을 유저에게 내보내야한다.


자, 여기서부터 문제
어떻게 제어를 해야할까??

일단, 한번 검색부터 해보자.

Controlling Volume Mixer

라는 글이 보인다.

그리고, 아래의 글도 많은 도움이 되었다.

C# (CSharp) MIXERLINECONTROLS Code Examples

자, 그럼 이제 본격적으로 하나 만들어보자.
화면은 아래와 같이 구성하였다.

시스템 비프음을 내고, 시스템사운드를 이용한 음향효과를 내보내는 긴 버튼
그리고, 해당 어플에서 음량을 100%로 효과발산(?) 해주는 버튼

스피커 음량의 초기셋팅은 아래와같이 지정해둔것을 전제로 한다.

스피커는 MAX 100% 지정되어있으며, 나머지는 모두 0% 로 지정 (소리를 받지않겠다는 강한의지!!)

일단, 여기저기 있는 함수를 끌어~ 끌어~모아서 baseClass 작성
baseClass는 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
using System;
using System.IO;
using System.Runtime.InteropServices;
 
namespace WindowsFormsApplication1
{
    public sealed class SoundUtils
    {
        public const int MMSYSERR_NOERROR = 0;
        public const int MAXPNAMELEN = 32;
        public const int MIXER_LONG_NAME_CHARS = 64;
        public const int MIXER_SHORT_NAME_CHARS = 16;
        public const int MIXER_GETLINEINFOF_COMPONENTTYPE = 0x3;
        public const int MIXER_GETCONTROLDETAILSF_VALUE = 0x0;
        public const int MIXER_GETLINECONTROLSF_ONEBYTYPE = 0x2;
        public const int MIXER_SETCONTROLDETAILSF_VALUE = 0x0;
        public const int MIXERLINE_COMPONENTTYPE_DST_FIRST = 0x0;
        public const int MIXERLINE_COMPONENTTYPE_SRC_FIRST = 0x1000;
        public const int MIXERLINE_COMPONENTTYPE_DST_SPEAKERS =
            (MIXERLINE_COMPONENTTYPE_DST_FIRST + 4);
        public const int MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE =
            (MIXERLINE_COMPONENTTYPE_SRC_FIRST + 3);
        public const int MIXERLINE_COMPONENTTYPE_SRC_LINE =
            (MIXERLINE_COMPONENTTYPE_SRC_FIRST + 2);
        public const int MIXERCONTROL_CT_CLASS_FADER = 0x50000000;
        public const int MIXERCONTROL_CT_UNITS_UNSIGNED = 0x30000;
        public const int MIXERCONTROL_CONTROLTYPE_FADER =
            (MIXERCONTROL_CT_CLASS_FADER | MIXERCONTROL_CT_UNITS_UNSIGNED);
        public const int MIXERCONTROL_CONTROLTYPE_VOLUME =
            (MIXERCONTROL_CONTROLTYPE_FADER + 1);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerClose(int hmx);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetControlDetailsA(int hmxobj, ref
            MIXERCONTROLDETAILS pmxcd, int fdwDetails);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetDevCapsA(int uMxId, MIXERCAPS
            pmxcaps, int cbmxcaps);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetID(int hmxobj, int pumxID, int
            fdwId);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetLineControlsA(int hmxobj, ref
            MIXERLINECONTROLS pmxlc, int fdwControls);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetLineInfoA(int hmxobj, ref
            MIXERLINE pmxl, int fdwInfo);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerGetNumDevs();
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerMessage(int hmx, int uMsg, int
            dwParam1, int dwParam2);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerOpen(out int phmx, int uMxId,
            int dwCallback, int dwInstance, int fdwOpen);
 
        [DllImport("winmm.dll", CharSet = CharSet.Ansi)]
        private static extern int mixerSetControlDetails(int hmxobj, ref
            MIXERCONTROLDETAILS pmxcd, int fdwDetails);
 
        [DllImport("winmm.dll", EntryPoint = "sndPlaySoundA")]
        private static extern int sndPlaySound(string lpszSoundName, int uFlags);
        public enum SND
        {
            SND_SYNC = 0x0000,/* play synchronously (default) */
            SND_ASYNC = 0x0001/* play asynchronously */
            SND_NODEFAULT = 0x0002/* silence (!default) if sound not found */
            SND_MEMORY = 0x0004/* pszSound points to a memory file */
            SND_LOOP = 0x0008/* loop the sound until next sndPlaySound */
            SND_NOSTOP = 0x0010/* don't stop any currently playing sound */
            SND_NOWAIT = 0x00002000/* don't wait if the driver is busy */
            SND_ALIAS = 0x00010000,/* name is a registry alias */
            SND_ALIAS_ID = 0x00110000/* alias is a pre d ID */
            SND_FILENAME = 0x00020000/* name is file name */
            SND_RESOURCE = 0x00040004/* name is resource name or atom */
            SND_PURGE = 0x0040,  /* purge non-static events for task */
            SND_APPLICATION = 0x0080  /* look for application specific association */
        }
 
        public struct MIXERCAPS
        {
            public int wMid;
            public int wPid;
            public int vDriverVersion;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)]
            public string szPname;
            public int fdwSupport;
            public int cDestinations;
        }
 
        public struct MIXERCONTROL
        {
            public int cbStruct;
            public int dwControlID;
            public int dwControlType;
            public int fdwControl;
            public int cMultipleItems;
            [MarshalAs(UnmanagedType.ByValTStr,
                 SizeConst = MIXER_SHORT_NAME_CHARS)]
            public string szShortName;
            [MarshalAs(UnmanagedType.ByValTStr,
                 SizeConst = MIXER_LONG_NAME_CHARS)]
            public string szName;
            public int lMinimum;
            public int lMaximum;
            [MarshalAs(UnmanagedType.U4, SizeConst = 10)]
            public int reserved;
        }
 
        public struct MIXERCONTROLDETAILS
        {
            public int cbStruct;
            public int dwControlID;
            public int cChannels;
            public int item;
            public int cbDetails;
            public IntPtr paDetails;
        }
 
        public struct MIXERCONTROLDETAILS_UNSIGNED
        {
            public int dwValue;
        }
 
        public struct MIXERLINE
        {
            public int cbStruct;
            public int dwDestination;
            public int dwSource;
            public int dwLineID;
            public int fdwLine;
            public int dwUser;
            public int dwComponentType;
            public int cChannels;
            public int cConnections;
            public int cControls;
            [MarshalAs(UnmanagedType.ByValTStr,
                 SizeConst = MIXER_SHORT_NAME_CHARS)]
            public string szShortName;
            [MarshalAs(UnmanagedType.ByValTStr,
                 SizeConst = MIXER_LONG_NAME_CHARS)]
            public string szName;
            public int dwType;
            public int dwDeviceID;
            public int wMid;
            public int wPid;
            public int vDriverVersion;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)]
            public string szPname;
        }
 
        public struct MIXERLINECONTROLS
        {
            public int cbStruct;
            public int dwLineID;
 
            public int dwControl;
            public int cControls;
            public int cbmxctrl;
            public IntPtr pamxctrl;
        }
 
        private static bool GetVolumeControl(int hmixer, int componentType,
            int ctrlType, out MIXERCONTROL mxc, out int vCurrentVol)
        {
            // This function attempts to obtain a mixer control. 
            // Returns True if successful. 
            MIXERLINECONTROLS mxlc = new MIXERLINECONTROLS();
            MIXERLINE mxl = new MIXERLINE();
            MIXERCONTROLDETAILS pmxcd = new MIXERCONTROLDETAILS();
            MIXERCONTROLDETAILS_UNSIGNED du = new
                MIXERCONTROLDETAILS_UNSIGNED();
            mxc = new MIXERCONTROL();
            int rc;
            bool retValue;
            vCurrentVol = -1;
 
            mxl.cbStruct = Marshal.SizeOf(mxl);
            mxl.dwComponentType = componentType;
 
            rc = mixerGetLineInfoA(hmixer, ref mxl,
                MIXER_GETLINEINFOF_COMPONENTTYPE);
 
            if (MMSYSERR_NOERROR == rc)
            {
                int sizeofMIXERCONTROL = 152;
                int ctrl = Marshal.SizeOf(typeof(MIXERCONTROL));
                mxlc.pamxctrl = Marshal.AllocCoTaskMem(sizeofMIXERCONTROL);
                mxlc.cbStruct = Marshal.SizeOf(mxlc);
                mxlc.dwLineID = mxl.dwLineID;
                mxlc.dwControl = ctrlType;
                mxlc.cControls = 1;
                mxlc.cbmxctrl = sizeofMIXERCONTROL;
 
                // Allocate a buffer for the control 
                mxc.cbStruct = sizeofMIXERCONTROL;
 
                // Get the control 
                rc = mixerGetLineControlsA(hmixer, ref mxlc,
                    MIXER_GETLINECONTROLSF_ONEBYTYPE);
 
                if (MMSYSERR_NOERROR == rc)
                {
                    retValue = true;
 
                    // Copy the control into the destination structure 
                    mxc = (MIXERCONTROL)Marshal.PtrToStructure(
                        mxlc.pamxctrl, typeof(MIXERCONTROL));
                }
                else
                {
                    retValue = false;
                }
                int sizeofMIXERCONTROLDETAILS =
                    Marshal.SizeOf(typeof(MIXERCONTROLDETAILS));
                int sizeofMIXERCONTROLDETAILS_UNSIGNED =
                    Marshal.SizeOf(typeof(MIXERCONTROLDETAILS_UNSIGNED));
                pmxcd.cbStruct = sizeofMIXERCONTROLDETAILS;
                pmxcd.dwControlID = mxc.dwControlID;
                pmxcd.paDetails =
 
                    Marshal.AllocCoTaskMem(sizeofMIXERCONTROLDETAILS_UNSIGNED);
                pmxcd.cChannels = 1;
                pmxcd.item = 0;
                pmxcd.cbDetails = sizeofMIXERCONTROLDETAILS_UNSIGNED;
 
                rc = mixerGetControlDetailsA(hmixer, ref pmxcd,
                    MIXER_GETCONTROLDETAILSF_VALUE);
 
                du = (MIXERCONTROLDETAILS_UNSIGNED)Marshal.PtrToStructure(
                    pmxcd.paDetails, typeof(MIXERCONTROLDETAILS_UNSIGNED));
 
                vCurrentVol = du.dwValue;
 
                return retValue;
            }
 
            retValue = false;
            return retValue;
        }
 
        private static bool SetVolumeControl(int hmixer, MIXERCONTROL mxc,
            int volume)
        {
            // This function sets the value for a volume control. 
            // Returns True if successful 
 
            bool retValue;
            int rc;
            MIXERCONTROLDETAILS mxcd = new MIXERCONTROLDETAILS();
            MIXERCONTROLDETAILS_UNSIGNED vol = new
                MIXERCONTROLDETAILS_UNSIGNED();
 
            mxcd.item = 0;
            mxcd.dwControlID = mxc.dwControlID;
            mxcd.cbStruct = Marshal.SizeOf(mxcd);
            mxcd.cbDetails = Marshal.SizeOf(vol);
 
            // Allocate a buffer for the control value buffer 
            mxcd.cChannels = 1;
            vol.dwValue = volume;
 
            // Copy the data into the control value buffer 
            mxcd.paDetails = Marshal.AllocCoTaskMem(Marshal.SizeOf(
                typeof(MIXERCONTROLDETAILS_UNSIGNED)));
            Marshal.StructureToPtr(vol, mxcd.paDetails, false);
 
            // Set the control value 
            rc = mixerSetControlDetails(hmixer, ref mxcd,
                MIXER_SETCONTROLDETAILSF_VALUE);
 
            if (MMSYSERR_NOERROR == rc)
            {
                retValue = true;
            }
            else
            {
                retValue = false;
            }
            return retValue;
        }
 
        /// <summary>
        /// 薄仙税 瑳拳聖 亜閃身
        /// </summary>
        /// <returns></returns>
        public static int GetVolume()
        {
            int mixer;
            int currentVol;
            MIXERCONTROL volCtrl = new MIXERCONTROL();
            mixerOpen(out mixer, 0000);
            int type = MIXERCONTROL_CONTROLTYPE_VOLUME;
            GetVolumeControl(mixer,
                MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out
                currentVol);
            mixerClose(mixer);
 
            return currentVol;
        }
 
        /// <summary>
        /// ボリューム設定
        /// </summary>
        /// <param name="vVolume"></param>
        public static void SetVolume(int vVolume)
        {
            int mixer;
            MIXERCONTROL volCtrl = new MIXERCONTROL();
            int currentVol;
            mixerOpen(out mixer, 0000);
            int type = MIXERCONTROL_CONTROLTYPE_VOLUME;
            GetVolumeControl(mixer,
                MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out
                currentVol);
            if (vVolume > volCtrl.lMaximum) vVolume = volCtrl.lMaximum;
            if (vVolume < volCtrl.lMinimum) vVolume = volCtrl.lMinimum;
            SetVolumeControl(mixer, volCtrl, vVolume);
            GetVolumeControl(mixer,
                MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out
                currentVol);
            if (vVolume != currentVol)
            {
                throw new Exception("Cannot Set Volume");
            }
            mixerClose(mixer);
        }
 
        /// <summary>
        /// ボリュームをパーセントで設定
        /// </summary>
        /// <param name="iPercent"></param>
        public static void SetVolumePercent(int iPercent)
        {
            int iVolumn = 0;
            int mixer;
            int currentVol;
 
            if (iPercent < 0)
                iPercent = 0;
            else if (iPercent > 100)
                iPercent = 100;
 
            MIXERCONTROL volCtrl = new MIXERCONTROL();
            mixerOpen(out mixer, 0000);
            int type = MIXERCONTROL_CONTROLTYPE_VOLUME;
            GetVolumeControl(mixer,
                MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out
                currentVol);
 
            iVolumn = volCtrl.lMaximum * iPercent / 100;
 
            SetVolumeControl(mixer, volCtrl, iVolumn);
            GetVolumeControl(mixer,
                MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out
                currentVol);
            if (iVolumn != currentVol)
            {
                throw new Exception("Cannot Set Volume");
            }
            mixerClose(mixer);
 
        }
 
        /// <summary>
        /// WAVファイルを流す
        /// </summary>
        /// <param name="filename">Wavファイルプルパス</param>
        /// <param name="loop">true:再生成功, false:再生失敗</param>
        public static void PlaySound(string filename, bool loop)
        {
            if (File.Exists(filename))
            {
                if (loop)
                    sndPlaySound(filename, (int)SND.SND_ASYNC | (int)SND.SND_LOOP);
                else
                    sndPlaySound(filename, (int)SND.SND_ASYNC);
            }
        }
 
        private Microsoft.VisualBasic.Devices.Audio audio = new Microsoft.VisualBasic.Devices.Audio();
 
        /// <summary>
        /// 指定したファイルを流す
        /// </summary>
        /// <param name="wavFilePath"></param>
        public void PlaySoundWav(string wavFilePath)
        {
            //バックグラウンドでWAVを再生する
            audio.Play(wavFilePath, Microsoft.VisualBasic.AudioPlayMode.Background);
 
            //次のようにすると、ループ再生される
            //audio.Play(wavFilePath,
            //    Microsoft.VisualBasic.AudioPlayMode.BackgroundLoop);
 
            //次のようにすると、最後まで再生し終えるまで待機する
            //audio.Play(wavFilePath,
            //    Microsoft.VisualBasic.AudioPlayMode.WaitToComplete);
 
            //再生しているWAVを停止する
            audio.Stop();
        }
    }
}
 
cs

화면의 코드는 아래와같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.Windows.Forms;
using System.Media;
using Microsoft.VisualBasic;
 
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            Console.Beep();
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            Interaction.Beep();
        }
        
        private void button3_Click(object sender, EventArgs e)
        {
            Console.Beep(10000500);
        }
        
        private void button4_Click(object sender, EventArgs e)
        {
            //SystemSounds.Beep.Play();
            //SystemSounds.Exclamation.Play();
            SystemSounds.Question.Play();
            //SystemSounds.Asterisk.Play();
            //SystemSounds.Hand.Play();
        }
 
        private void button5_Click(object sender, EventArgs e)
        {
            SoundUtils.SetVolumePercent(100);
        }
 
        private void button6_Click(object sender, EventArgs e)
        {
            SoundUtils.SetVolumePercent(50);
        }
 
        private void button7_Click(object sender, EventArgs e)
        {
            SoundUtils.SetVolumePercent(0);
        }
 
        private void button8_Click(object sender, EventArgs e)
        {
            SoundUtils.GetVolume();
 
            SoundUtils.SetVolume(100);
        }
    }
}
 
cs


실행시켜보면, 사운드 믹서에 해당어플이 등록되어서 볼륨제어가 가능해진다.

[실행전]

[실행후] & 100% 눌렀을때,

[실행후] & 50% 눌렀을때,

[실행후] & 0% 눌렀을때,


다른어플에 대해서도 어플ID 및 ProcessName 등을 통해서 사운드를 제어할수 있다.

추후과제

어플의 초기 기동시에 전체 권한을 부여받아서 Mute 를 해제하고,

Speaker 의 Sound Volume을 MAX 로 지정하고,

타어플에 대해서는 모든 Volume를 Min 0% 로 지정한 후,

지금의 어플동작을 실시하면, 모든 과제는 해결될 것으로 보여진다.

지금까지 만든 소스는 아래에 첨부한다.

SoundUtils.zip

SoundUtils.zip


password = toshu1203

질문

비주얼베이직 폼에 텍스트박스가 다른 폼 레이블 실명인증 받은 성인

프로필이미지비공개
질문1건
질문마감률0%
질문채택률0%
2018.10.07. 00:55
조회수5
form 1과 form 2가 있는데 form1에 텍스트박스에 숫자를 넣고 button을 누르면 form2의 창이 뜨면서 form2의 레이블에 숫자가 있고 form2의 버튼을 누르면 1씩 감소되는 것에 대한 코딩을 어떻게 하나요? 
form1의 텍스트박스에 적힌 숫자를 form2의 레이블에 그대로 옮겨지게 되는 방법을 뭘까요?


위와 같은 질문이 있어서, 간단하게 만들어본 샘플코드

우선, 폼을 두개 배치하고

[폼1]


[폼2]

둘다 같은 디자인을 합니다.


이후, 코드제어

[폼1]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using System;
using System.Windows.Forms;
 
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            if (textBox1.Text != string.Empty)
            {
                Form2 frm2 = new Form2(textBox1.Text);
                frm2.Show();
            }
            else
            {
                MessageBox.Show("텍스트박스에 값이 없습니다.");
            }
        }
    }
}
 
cs


[폼2]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Windows.Forms;
 
namespace WindowsFormsApplication1
{
    public partial class Form2 : Form
    {
        string Param = string.Empty;
 
        public Form2(string param)
        {
            Param = param;
            InitializeComponent();
        }
 
        private void Form2_Load(object sender, EventArgs e)
        {
            this.textBox1.Text = Param;
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            if (textBox1.Text == string.Empty)
            {
                MessageBox.Show("텍스트박스에 값이 없습니다.");
            }
            else
            {
                textBox1.Text = (Convert.ToInt32(textBox1.Text) - 1).ToString();
            }
        }
    }
}
 
cs


실행화면은 아래와 같습니다.


3을 입력

버튼클릭하면, 폼2가 실행됩니다.

버튼을 눌러봅시다.