Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 7개 있습니다.)

x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제

아래와 같은 질문이 있군요,

윈폼 기반의 응용프로그램 dll 참조와 32,64bit 빌드 관련 문의
; https://www.sysnet.pe.kr/3/0/5484

간단하게 문제를 재현해 볼까요? 우선, Windows Forms App 프로젝트(EXE)와 Windows Forms Control Library 프로젝트(DLL)를 하나씩 만듭니다. 그리곤 해당 DLL 프로젝트를 WinForm 앱에서 참조한 후 Form1 디자이너에 (DLL의) UserControl을 올려놓으면 다음과 같이 보일 것입니다.

x64_uicontrol_in_designer_1.png

이 상태에서, UserControl을 담고 있는 DLL 프로젝트만 속성 창에서 (AnyCPU에서) x64로 바꾼 후 다시 빌드하면 EXE 프로젝트의 Form 디자인 창에 다음과 같은 오류가 발생합니다.

x64_uicontrol_in_designer_2.png

To prevent possible data loss before loading the designer, the following errors must be resolved:

The variable 'userControl1' is either undeclared or was never assigned.

오류 메시지에서 보이는 "WindowsFormsApp1 Form1.Designer.cs Line: 50 Column: 1"의 소스 코드는 DLL의 User Control을 추가하는 라인입니다.

// Form1.Designer.cs

// ...[생략]...
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(431, 317);
this.Controls.Add(this.userControl11);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
// ...[생략]...

위의 화면에서 "Show Call Stack" 링크를 누르면 이런 메시지가 보입니다.

at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.Error(IDesignerSerializationManager manager, String exceptionText, String helpLink)
at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.DeserializeExpression(IDesignerSerializationManager manager, String name, CodeExpression expression)
at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.DeserializeExpression(IDesignerSerializationManager manager, String name, CodeExpression expression)
at System.ComponentModel.Design.Serialization.CodeDomSerializerBase.DeserializeStatement(IDesignerSerializationManager manager, CodeStatement statement)  

그러니까 상황을 유추해 보면 "userControl1"이 undeclared 또는 assigned 되지 않았다는 것에서 해당 컨트롤을 생성하는 데 실패했지만 아마도 내부적인 try/catch로 인해 그 시점에 발생한 예외는 무시했을 것입니다. 하지만, 이후 this.Controls.Add에 null을 전달한 격이므로 그제서야 폼 디자이너는 더 이상의 실행을 할 수 없었던 것이고.




이에 대한 근본적인 원인은, Visual Studio 프로세스(devenv.exe)가 32비트라는 점입니다. 즉, x86 프로세스에서 구현한 폼 디자이너 화면에서 x64 용으로 빌드한 DLL을 로딩하는 것이 불가능하므로 저런 예외가 발생하는 것입니다.

따라서 해결책은, Windows Forms Control Library의 빌드 타깃을 (내부적으로 x86은 지원하지 않아 강제로 x64로 설정하는 것이 맞다 할지라도) AnyCPU로 지정하는 것입니다.

사실 그래도 큰 무리가 없는 것이, DLL은 그것이 활성화되는 EXE 프로세스의 플랫폼을 따라가게 되므로 어차피 활성화되는 EXE 프로젝트가 x64로 설정되어 있다면 DLL 측에서 AnyCPU로 되어 있다고 해서 문제가 되는 것은 없기 때문입니다.




그런데, 여기서 재미있는 점이 하나 있습니다. 저런 제약 사항은, 위와 같은 식으로 Windows Forms App에서 참조하는 다른 x64 DLL의 컨트롤을 사용할 때에만 폼 디자이너에서 오류가 발생한다는 점입니다.

즉, x64 타깃의 Windows Forms Control Library 프로젝트 내에서 해당 User Control을 폼 디자이너에서 여는 것은 허용이 됩니다. 그래서, 다음과 같이 디자인할 수 있습니다.

x64_uicontrol_in_designer_3.png

마찬가지로 위의 화면에서 보이는 "WindowsFormsApp1" 프로젝트를 x64 빌드로 바꾼 후, Form1에 대해 디자인 창을 열어도 잘 열립니다. 아니... devenv.exe 32비트 프로세스에서 어떻게 x64 프로젝트가 담고 있는 클래스의 폼을 디자인할 수 있게 만들었을까요?

그 이유는, 비주얼 스튜디오가 특별히 해당 프로젝트 내에서의 디자인 시에는 InitializeComponent 내의 코드를 별도의 과정을 거쳐 빌드하기 때문으로 보입니다. 즉, x64 프로젝트이지만 InitializeComponent의 코드를 x86 대상의 빌드로 컴파일해 디자인 시에만 사용하는 것입니다.

이에 대한 확인을 InitializeComponent에서 다음과 같은 코드를 추가해보면 됩니다.

private void InitializeComponent()
{
    this.userControl11 = new WindowsFormsControlLibrary1.UserControl1();
    this.SuspendLayout();
    // 
    // userControl11
    // 
    this.userControl11.Location = new System.Drawing.Point(27, 26);
    this.userControl11.Name = "userControl11";
    this.userControl11.Size = new System.Drawing.Size(227, 140);
    this.userControl11.TabIndex = 0;
    // 
    // Form1
    // 
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.ClientSize = new System.Drawing.Size(431, 317);
    this.Controls.Add(this.userControl11);
    this.Name = "Form1";
    this.Text = "Form1";
    this.Load += new System.EventHandler(this.Form1_Load);
    this.ResumeLayout(false);

    this.Text = IntPtr.Size.ToString();
}

이렇게 하고 폼 디자이너를 열면 다음과 같이 (x64 타깃이 상황에서도 8이 아닌) 4가 찍힙니다.

x64_uicontrol_in_designer_4.png

위의 사실에 기반을 둔다면, 참조한 DLL의 컨트롤에 대해 오류가 나는 것을 일면 이해할 수도 있습니다. 디자인 목적으로 사용하기 위해 특별히 InitializeComponent를 내부적으로 빌드하지만, 그 코드에서 사용하고 있는 DLL이 x64로 되어 있다면 당연히 해당 컨트롤을 생성할 수 없을 것입니다.

어찌 보면, 비주얼 스튜디오 개발자들도 x86 환경에서 x64 응용 프로그램을 개발할 수 있도록 나름 고군분투하는 노력을 하고 있었던 것입니다. ^^




또 한가지 재미있는 점을 볼까요?

마이크로소프트는, InitializeComponent 메서드의 코드가 디자이너에 의해 바뀌는 것이므로 개발자로 하여금 변경하지 않을 것을 권장합니다.

#region Component Designer generated code

/// <summary>
/// Required method for Designer support - do not modify 
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
    this.SuspendLayout();
    // 
    // UserControl1
    // 
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.Name = "UserControl1";
    this.Size = new System.Drawing.Size(391, 230);
    this.ResumeLayout(false);

}

#endregion

그런데, "권장"하는 수준이 아닌 강제로 하지 못하게 만드는 것도 있습니다. 일례로 다음과 같이 if 문을 추가하면,

private void InitializeComponent()
{
    this.SuspendLayout();
    // 
    // UserControl1
    // 
    this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
    this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    this.Name = "UserControl1";
    this.Size = new System.Drawing.Size(391, 230);
    this.ResumeLayout(false);
    if (string.IsNullOrEmpty(this.Name) != null) { }
}

폼 디자인 창에서는 이런 오류가 발생합니다.

The designer cannot process the code at line 51: if (string.IsNullOrEmpty(this.Name) != null) { } The code within the method 'InitializeComponent' is generated by the designer and should not be manually modified. Please remove any changes and try opening the designer again.


이때의 호출 스택을 보면 일련의 수수께끼들이 풀립니다.

at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.CreateQuoteExpression(XmlElementData xmlElement)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.XmlElementData.get_CodeDomElement()
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.EndElement(String prefix, String name, String urn)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.Parse(XmlReader reader)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.XML.CodeDomXmlProcessor.ParseXml(String xmlStream, CodeStatementCollection statementCollection, String fileName, String methodName)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomParser.OnMethodPopulateStatements(Object sender, EventArgs e)
at System.CodeDom.CodeMemberMethod.get_Statements()
at System.ComponentModel.Design.Serialization.TypeCodeDomSerializer.Deserialize(IDesignerSerializationManager manager, CodeTypeDeclaration declaration)
at System.ComponentModel.Design.Serialization.CodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager manager)
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager serializationManager)
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.VisualStudio.Design.Serialization.CodeDom.VSCodeDomDesignerLoader.PerformLoad(IDesignerSerializationManager serializationManager)
at System.ComponentModel.Design.Serialization.BasicDesignerLoader.BeginLoad(IDesignerLoaderHost host) 

보는 바와 같이, x86 프로세스인 비주얼 스튜디오의 폼 디자인에서 x64 프로젝트의 컨트롤 디자인이 가능한 이유가 내부적으로 특별하게 VSCodeDomParser/CodeDomXmlProcessor를 이용해 임의로 코드를 빌드하고 있었기 때문입니다. 그리고 VSCodeDomParser/CodeDomXmlProcessor는 대입문이나 생성문, 메서드 호출 등의 제한적인 코드만 허용하기 때문에 if 문을 담고 있으면 저렇게 오류가 발생하는 것입니다.

(거짓이지만) 백조는 물 밑에서 쉴 새 없이 발길질을 한다는 표현이 딱 어울리는군요. ^^;




참고로, 이런 문제들은 x64 프로세스의 devenv.exe가 나와도 접할 수 있는 것들입니다.

그때가 되면, 아마도 일상적으로는 64비트 비주얼 스튜디오를 띄우겠고 그러다 컨트롤의 DLL을 x86 빌드 용으로 바꾸면 저런 오류를 보게 될 것입니다. 그럼 해당 문제를 해결하기 위해 현재의 비주얼 스튜디오를 종료하고 32비트 비주얼 스튜디오를 띄워 디자인 창을 열어야 할 테고!

(업데이트 2021-04-20: 비주얼 스튜디오 2022는 64비트 버전이라고 합니다. ^^ https://devblogs.microsoft.com/visualstudio/visual-studio-2022/) 그나저나, 과연 비주얼 스튜디오 64비트 버전이 나올 수 있을까요? 현재 아래에서 요청이 이뤄지고 있으니 관심 있으신 분은 투표를 해도 좋겠습니다.

Visual Studio x64 implementation - Visual Studio Feedback 12
; https://developercommunity.visualstudio.com/t/visual-studio-x64-implementation/1372671

하지만 요청자의 진지한 요구 사항이 "We need VS x64. We need VS that can take more than ~2GB of RAM."이라고 하는데요, 사실 이로 인한 것이 전부라면 마이크로소프트가 이번에도 drop할 가능성이 있습니다. 왜냐하면, 비주얼 스튜디오는 그동안 많은 구성 요소를 devenv.exe 프로세스 내에서 떼어내 별도의 프로세스로 분리시켜왔기 때문에 devenv.exe 자체가 2GB를 넘는 메모리를 소비할 가능성이 거의 없습니다.

눈치 빠르신 분들은 비주얼 스튜디오를 하나 실행시켰을 뿐인데 프로젝트가 진행되면서 아래의 프로세스들이 함께 뜨는 것을 보셨을 것입니다.

  • Microsoft.Alm.Shared.Remoting.RemoteContainer
  • Microsoft.ServiceHub.Controller
  • vsls-agent
  • MSBuild
  • PerfWatson2
  • ServiceHub.RoslynCodeAnalysisService
  • ServiceHub.TestWindowStoreHost
  • ServiceHub.Host.CLR.x86
  • ServiceHub.IdentityHost
  • ServiceHub.ThreadedWaitDialog
  • ServiceHub.VSDetouredHost

즉, 예전에는 하나의 프로세스에서 처리했던 것을 메모리의 한계로 분리를 하면서 저렇게 많은 프로세스로 늘어난 것입니다. (물론 신규 기능도 있겠지만.)

그래서 이제는 웬만큼 규모가 큰 - 로딩하는 데만 시간이 오래 걸릴 정도로 다수의 프로젝트를 한데 집어넣은 솔루션이 아니고서는 메모리 한계를 넘을 일은 거의 없습니다. 더군다나 시대도 이젠 마이크로서비스를 지향하면서 솔루션 파일들이 나뉘는 것도 한몫을 하고 있고.

단지, 기능 분할에 있어 한 가지 문제가 있다면, 그것이 불가능한 구성 요소가 바로 UI라는 점입니다. 일단은 마이크로소프트가 XAML 디자인에 한해 x64 문제를 해결하려는 노력은 하고 있는 중입니다.

XAML Designer
; https://devblogs.microsoft.com/visualstudio/improvements-to-xaml-tooling-in-visual-studio-2019-version-16-7-preview-1/#xaml-designer

현재에도 preview 단계이므로 이를 활성화하려면 "Tools" / "Options" 창에서 "Preview Features and select "New WPF XAML Designer for .NET Framework"" 항목을 선택 후 재시작을 해야 합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 4/20/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2023-02-24 01시02분
정성태

1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...
NoWriterDateCnt.TitleFile(s)
13272정성태2/27/20234216오류 유형: 850. SSMS - mdf 파일을 Attach 시킬 때 Operating system error 5: "5(Access is denied.)" 에러
13271정성태2/25/20234149오류 유형: 849. Sql Server Configuration Manager가 시작 메뉴에 없는 경우
13270정성태2/24/20233758.NET Framework: 2098. dotnet build에 /p 옵션을 적용 시 유의점
13269정성태2/23/20234295스크립트: 46. 파이썬 - uvicorn의 콘솔 출력을 UDP로 전송
13268정성태2/22/20234849개발 환경 구성: 667. WSL 2 내부에서 열고 있는 UDP 서버를 호스트 측에서 접속하는 방법
13267정성태2/21/20234774.NET Framework: 2097. C# - 비동기 소켓 사용 시 메모리 해제가 finalizer 단계에서 발생하는 사례파일 다운로드1
13266정성태2/20/20234376오류 유형: 848. .NET Core/5+ - Process terminated. Couldn't find a valid ICU package installed on the system
13265정성태2/18/20234287.NET Framework: 2096. .NET Core/5+ - PublishSingleFile 유형에 대한 runtimeconfig.json 설정
13264정성태2/17/20235785스크립트: 45. 파이썬 - uvicorn 사용자 정의 Logger 작성
13263정성태2/16/20233918개발 환경 구성: 666. 최신 버전의 ilasm.exe/ildasm.exe 사용하는 방법
13262정성태2/15/20235006디버깅 기술: 191. dnSpy를 이용한 (소스 코드가 없는) 닷넷 응용 프로그램 디버깅 방법 [1]
13261정성태2/15/20234299Windows: 224. Visual Studio - 영문 폰트가 Fullwidth Latin Character로 바뀌는 문제
13260정성태2/14/20234083오류 유형: 847. ilasm.exe 컴파일 오류 - error : syntax error at token '-' in ... -inf
13259정성태2/14/20234209.NET Framework: 2095. C# - .NET5부터 도입된 CollectionsMarshal
13258정성태2/13/20234107오류 유형: 846. .NET Framework 4.8 Developer Pack 설치 실패 - 0x81f40001
13257정성태2/13/20234204.NET Framework: 2094. C# - Job에 Process 포함하는 방법 [1]파일 다운로드1
13256정성태2/10/20235056개발 환경 구성: 665. WSL 2의 네트워크 통신 방법 - 두 번째 이야기
13255정성태2/10/20234350오류 유형: 845. gihub - windows2022 이미지에서 .NET Framework 4.5.2 미만의 프로젝트에 대한 빌드 오류
13254정성태2/10/20234262Windows: 223. (WMI 쿼리를 위한) PowerShell 문자열 escape 처리
13253정성태2/9/20235040Windows: 222. C# - 다른 윈도우 프로그램이 실행되었음을 인식하는 방법파일 다운로드1
13252정성태2/9/20233852오류 유형: 844. ssh로 명령어 수행 시 멈춤 현상
13251정성태2/8/20234318스크립트: 44. 파이썬의 3가지 스레드 ID
13250정성태2/8/20236114오류 유형: 843. System.InvalidOperationException - Unable to configure HTTPS endpoint
13249정성태2/7/20234927오류 유형: 842. 리눅스 - You must wait longer to change your password
13248정성태2/7/20234048오류 유형: 841. 리눅스 - [사용자 계정] is not in the sudoers file. This incident will be reported.
13247정성태2/7/20234962VS.NET IDE: 180. Visual Studio - 닷넷 소스 코드 디버깅 중 "Decompile source code"가 동작하는 않는 문제
1  2  3  4  5  6  7  8  9  10  11  12  13  [14]  15  ...