Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

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"" 항목을 선택 후 재시작을 해야 합니다.




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

[연관 글]


donaricano-btn



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

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

비밀번호

댓글 쓴 사람
 




1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
12715정성태7/17/2021896오류 유형: 736. Windows - MySQL zip 파일 버전의 "mysqld --skip-grant-tables" 실행 시 비정상 종료
12714정성태7/16/2021237오류 유형: 735. VCRUNTIME140.dll, MSVCP140.dll, VCRUNTIME140.dll, VCRUNTIME140_1.dll이 없어 exe 실행이 안 되는 경우
12713정성태7/16/2021283.NET Framework: 1077. C# - 동기 방식이면서 비동기 규약을 따르게 만드는 Task.FromResult파일 다운로드1
12712정성태7/15/2021190개발 환경 구성: 579. Azure - 리눅스 호스팅의 Site Extension 제작 방법
12711정성태7/15/2021198개발 환경 구성: 578. Azure - Java Web App Service를 위한 Site Extension 제작 방법
12710정성태7/15/2021264개발 환경 구성: 577. MQTT - emqx.io 서비스 소개
12709정성태7/14/2021143Linux: 42. 실행 중인 docker 컨테이너에 대한 구동 시점의 docker run 명령어를 확인하는 방법
12708정성태7/14/2021239Linux: 41. 리눅스 환경에서 디스크 용량 부족 시 원인 분석 방법
12707정성태7/14/2021395오류 유형: 734. MySQL - Authentication method 'caching_sha2_password' not supported by any of the available plugins.
12706정성태7/14/2021342.NET Framework: 1076. C# - AsyncLocal 기능을 CallContext만으로 구현하는 방법 [1]파일 다운로드1
12705정성태7/13/2021236VS.NET IDE: 168. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제 - 두 번째 이야기
12704정성태7/12/2021171개발 환경 구성: 576. Azure VM의 서비스를 Azure Web App Service에서만 접근하도록 NSG 설정을 제한하는 방법
12703정성태7/11/2021216개발 환경 구성: 575. Azure VM에 (ICMP) ping을 허용하는 방법
12702정성태7/11/2021173오류 유형: 733. TaskScheduler에 등록된 wacs.exe의 Let's Encrypt 인증서 업데이트 문제
12701정성태7/9/2021210.NET Framework: 1075. C# - ThreadPool의 스레드는 반환 시 ThreadStatic과 AsyncLocal 값이 초기화 될까요?파일 다운로드1
12700정성태7/8/2021343.NET Framework: 1074. RuntimeType의 메모리 누수? [1]
12699정성태7/8/2021263VS.NET IDE: 167. Visual Studio 디버깅 중 GC Heap 상태를 보여주는 "Show Diagnostic Tools" 메뉴 사용법
12698정성태7/7/2021523오류 유형: 732. Windows 11 업데이트 시 3% 또는 0%에서 다운로드가 멈춘 경우
12697정성태7/7/2021397개발 환경 구성: 574. Windows 11 (Insider Preview) 설치하는 방법
12696정성태7/6/2021312VC++: 146. 운영체제의 스레드 문맥 교환(Context Switch)을 유사하게 구현하는 방법파일 다운로드2
12695정성태7/3/2021296VC++: 145. C 언어의 setjmp/longjmp 기능을 Thread Context를 이용해 유사하게 구현하는 방법파일 다운로드1
12694정성태7/2/2021154Java: 24. Azure - Spring Boot 앱을 Java SE(Embedded Web Server)로 호스팅 시 로그 파일 남기는 방법
12693정성태6/30/2021161오류 유형: 731. Azure Web App Site Extension - Failed to install web app extension [...]. {1}
12692정성태6/30/2021317디버깅 기술: 180. Azure - Web App의 비정상 종료 시 남겨지는 로그 확인
12691정성태6/30/2021223개발 환경 구성: 573. 테스트 용도이지만 테스트에 적합하지 않은 Azure D1 공유(shared) 요금제
12690정성태6/28/2021364Java: 23. Azure - 자바(Java)로 만드는 Web App Service - Tomcat 호스팅
1  2  3  4  5  [6]  7  8  9  10  11  12  13  14  15  ...