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)
12690정성태6/28/2021371Java: 23. Azure - 자바(Java)로 만드는 Web App Service - Tomcat 호스팅
12689정성태6/25/2021337오류 유형: 730. Windows Forms 디자이너 - The class Form1 can be designed, but is not the first class in the file.
12688정성태6/24/2021586.NET Framework: 1073. C# - JSON 역/직렬화 시 리플렉션 손실을 없애는 JsonSrcGen [1]파일 다운로드1
12687정성태6/22/2021218오류 유형: 729. Invalid data: Invalid artifact, java se app service only supports .jar artifact
12686정성태6/21/2021287Java: 22. Azure - 자바(Java)로 만드는 Web App Service - Java SE (Embedded Web Server) 호스팅
12685정성태6/21/2021324Java: 21. Azure Web App Service에 배포된 Java 프로세스의 메모리 및 힙(Heap) 덤프 뜨는 방법
12684정성태6/19/2021297오류 유형: 728. Visual Studio 2022부터 DTE.get_Properties 속성 접근 시 System.MissingMethodException 예외 발생
12683정성태6/18/2021394VS.NET IDE: 166. Visual Studio 2022 - Windows Forms 프로젝트의 x86 DLL 컨트롤이 Designer에서 오류가 발생하는 문제파일 다운로드1
12682정성태6/18/2021334VS.NET IDE: 165. Visual Studio 2022를 위한 Extension 마이그레이션
12681정성태6/18/2021288오류 유형: 727. .NET 2.0 ~ 3.5 + x64 환경에서 System.EnterpriseServices 참조 시 CS8012 경고
12680정성태6/18/2021312오류 유형: 726. python2.7.exe 실행 시 0xc000007b 오류
12679정성태6/18/2021334COM 개체 관련: 23. CoInitializeSecurity의 전역 설정을 재정의하는 CoSetProxyBlanket 함수 사용법파일 다운로드1
12678정성태6/17/2021343.NET Framework: 1072. C# - CoCreateInstance 관련 Inteop 오류 정리파일 다운로드1
12677정성태6/17/2021335VC++: 144. 역공학을 통한 lxssmanager.dll의 ILxssSession 사용법 분석파일 다운로드1
12676정성태6/16/2021335VC++: 143. ionescu007/lxss github repo에 공개된 lxssmanager.dll의 CLSID_LxssUserSession/IID_ILxssSession 사용법파일 다운로드1
12675정성태6/16/2021288Java: 20. maven package 명령어 결과물로 (war가 아닌) jar 생성 방법
12674정성태6/15/2021291VC++: 142. DEFINE_GUID 사용법
12673정성태6/15/2021408Java: 19. IntelliJ - 자바(Java)로 만드는 Web App을 Tomcat에서 실행하는 방법
12672정성태6/15/2021360오류 유형: 725. IntelliJ에서 Java webapp 실행 시 "Address localhost:1099 is already in use" 오류
12671정성태6/15/2021545오류 유형: 724. Tomcat 실행 시 Failed to initialize connector [Connector[HTTP/1.1-8080]] 오류
12670정성태6/13/2021327.NET Framework: 1071. DLL Surrogate를 이용한 Out-of-process COM 개체에서의 CoInitializeSecurity 문제파일 다운로드1
12669정성태6/11/2021445.NET Framework: 1070. 사용자 정의 GetHashCode 메서드 구현은 C# 9.0의 record 또는 리팩터링에 맡기세요.
12668정성태6/11/2021539.NET Framework: 1069. C# - DLL Surrogate를 이용한 Out-of-process COM 개체 제작파일 다운로드2
12667정성태6/10/2021421.NET Framework: 1068. COM+ 서버 응용 프로그램을 이용해 CoInitializeSecurity 제약 해결파일 다운로드1
12666정성태6/10/2021405.NET Framework: 1067. 별도 DLL에 포함된 타입을 STAThread Main 메서드에서 사용하는 경우 CoInitializeSecurity 자동 호출파일 다운로드1
12665정성태6/9/2021347.NET Framework: 1066. Wslhub.Sdk 사용으로 알아보는 CoInitializeSecurity 사용 제약파일 다운로드1
1  2  3  4  5  6  [7]  8  9  10  11  12  13  14  15  ...