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)
12625정성태5/3/2021716오류 유형: 714. error CS5001: Program does not contain a static 'Main' method suitable for an entry point
12624정성태5/2/2021911.NET Framework: 1055. C# - struct/class가 스택/힙에 할당되는 사례 정리 [6]파일 다운로드1
12623정성태5/2/2021714.NET Framework: 1054. C# 9 최상위 문에 STAThread 사용 [1]파일 다운로드1
12622정성태5/2/2021521오류 유형: 713. XSD 파일을 포함한 프로젝트 - The type or namespace name 'TypedTableBase<>' does not exist in the namespace 'System.Data'
12621정성태5/1/2021880.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법파일 다운로드1
12620정성태4/29/20211191.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/20211037.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [1]파일 다운로드1
12618정성태4/27/2021680사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신파일 다운로드1
12617정성태4/26/2021580.NET Framework: 1050. C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/2021601.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/2021509오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
12614정성태4/24/2021864개발 환경 구성: 570. C# - Azure AD 인증을 지원하는 ASP.NET Core/5+ 웹 애플리케이션 예제 구성 [3]파일 다운로드1
12613정성태4/23/2021637.NET Framework: 1048. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드파일 다운로드1
12612정성태4/23/2021631.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
12611정성태4/22/2021578오류 유형: 711. 닷넷 EXE 실행 오류 - Mixed mode assembly is build against version 'v2.0.50727' of the runtime
12610정성태4/22/2021671.NET Framework: 1046. C# - 컴파일 시점에 참조할 수 없는 타입을 포함한 이벤트 핸들러를 Reflection을 이용해 구독하는 방법파일 다운로드1
12609정성태4/22/2021818.NET Framework: 1045. C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법파일 다운로드1
12608정성태4/21/2021986.NET Framework: 1044. C# - Generic Host를 이용해 .NET 5로 리눅스 daemon 프로그램 만드는 방법 [7]파일 다운로드1
12607정성태4/21/2021769.NET Framework: 1043. C# - 실행 시점에 동적으로 Delegate 타입을 만드는 방법파일 다운로드1
12606정성태4/21/2021768.NET Framework: 1042. C# - enum 값을 int로 암시적(implicit) 형변환하는 방법? [2]파일 다운로드1
12605정성태4/18/2021688.NET Framework: 1041. C# - AssemblyID, ModuleID를 관리 코드에서 구하는 방법파일 다운로드1
12604정성태4/18/2021634VS.NET IDE: 163. 비주얼 스튜디오 속성 창의 "Build(빌드)" / "Configuration(구성)"에서의 "활성" 의미
12603정성태4/16/2021664VS.NET IDE: 162. 비주얼 스튜디오 - 상속받은 컨트롤이 디자인 창에서 지원되지 않는 문제
12602정성태4/16/2021917VS.NET IDE: 161. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제
12601정성태4/15/2021646.NET Framework: 1040. C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
12600정성태4/15/2021726.NET Framework: 1039. C# - Kubeconfig의 token 설정 및 인증서 구성을 자동화하는 프로그램
1  2  3  4  5  6  7  8  9  [10]  11  12  13  14  15  ...