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)
13247정성태2/7/20234962VS.NET IDE: 180. Visual Studio - 닷넷 소스 코드 디버깅 중 "Decompile source code"가 동작하는 않는 문제
13246정성태2/6/20234085개발 환경 구성: 664. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 - 두 번째 이야기
13245정성태2/6/20234631.NET Framework: 2093. C# - PEM 파일을 이용한 RSA 개인키/공개키 설정 방법파일 다운로드1
13244정성태2/5/20233985VS.NET IDE: 179. Visual Studio - External Tools에 Shell 내장 명령어 등록
13243정성태2/5/20234855디버깅 기술: 190. windbg - Win32 API 호출 시점에 BP 거는 방법 [1]
13242정성태2/4/20234296디버깅 기술: 189. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.UnauthorizedAccessException
13241정성태2/3/20233825디버깅 기술: 188. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.IO.FileNotFoundException
13240정성태2/1/20233985디버깅 기술: 187. ASP.NET Web Application (.NET Framework) 프로젝트의 숨겨진 예외 - System.Web.HttpException
13239정성태2/1/20233627디버깅 기술: 186. C# - CacheDependency의 숨겨진 예외 - System.Web.HttpException
13238정성태1/31/20235636.NET Framework: 2092. IIS 웹 사이트를 TLS 1.2 또는 TLS 1.3 프로토콜로만 운영하는 방법
13237정성태1/30/20235322.NET Framework: 2091. C# - 웹 사이트가 어떤 버전의 TLS/SSL을 지원하는지 확인하는 방법
13236정성태1/29/20234967개발 환경 구성: 663. openssl을 이용해 인트라넷 IIS 사이트의 SSL 인증서 생성
13235정성태1/29/20234510개발 환경 구성: 662. openssl - 윈도우 환경의 명령행에서 SAN 적용하는 방법
13234정성태1/28/20235558개발 환경 구성: 661. dnSpy를 이용해 소스 코드가 없는 .NET 어셈블리의 코드를 변경하는 방법 [1]
13233정성태1/28/20236898오류 유형: 840. C# - WebClient로 https 호출 시 "The request was aborted: Could not create SSL/TLS secure channel" 예외 발생
13232정성태1/27/20234698스크립트: 43. uwsgi의 --processes와 --threads 옵션
13231정성태1/27/20233615오류 유형: 839. python - TypeError: '...' object is not callable
13230정성태1/26/20234030개발 환경 구성: 660. WSL 2 내부로부터 호스트 측의 네트워크로 UDP 데이터가 1개의 패킷으로만 제한되는 문제
13229정성태1/25/20234971.NET Framework: 2090. C# - UDP Datagram의 최대 크기
13228정성태1/24/20235118.NET Framework: 2089. C# - WMI 논리 디스크가 속한 물리 디스크의 정보를 얻는 방법 [2]파일 다운로드1
13227정성태1/23/20234822개발 환경 구성: 659. Windows - IP MTU 값을 바꿀 수 있을까요? [1]
13226정성태1/23/20234495.NET Framework: 2088. .NET 5부터 지원하는 GetRawSocketOption 사용 시 주의할 점
13225정성태1/21/20233747개발 환경 구성: 658. Windows에서 실행 중인 소켓 서버를 다른 PC 또는 WSL에서 접속할 수 없는 경우
13224정성태1/21/20234091Windows: 221. Windows - Private/Public/Domain이 아닌 네트워크 어댑터 단위로 방화벽을 on/off하는 방법
13223정성태1/20/20234283오류 유형: 838. RDP 연결 오류 - The two computers couldn't connect in the amount of time allotted
13222정성태1/20/20233934개발 환경 구성: 657. WSL - DockerDesktop.vhdx 파일 위치를 옮기는 방법
1  2  3  4  5  6  7  8  9  10  11  12  13  14  [15]  ...