Skip to content

Commit 80a0100

Browse files
committed
Add sample for hosting WinForms inside native DLL
1 parent 9779755 commit 80a0100

9 files changed

+251
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,4 @@ pkg/
343343

344344
# Ionide settings
345345
symbolCache.db
346+
/samples/HostedWindowsForms/loader.exe

WinFormsComInterop.sln

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Web.WebView2.Core
5252
EndProject
5353
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "SampleVisualBasic", "samples\SampleVisualBasic\SampleVisualBasic.vbproj", "{C5613BDA-F0DF-47DA-B76E-6C477AA61DB1}"
5454
EndProject
55+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HostedWindowsForms", "samples\HostedWindowsForms\HostedWindowsForms.csproj", "{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}"
56+
EndProject
5557
Global
5658
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5759
Debug|Any CPU = Debug|Any CPU
@@ -310,6 +312,22 @@ Global
310312
{C5613BDA-F0DF-47DA-B76E-6C477AA61DB1}.Release|x64.Build.0 = Release|Any CPU
311313
{C5613BDA-F0DF-47DA-B76E-6C477AA61DB1}.Release|x86.ActiveCfg = Release|Any CPU
312314
{C5613BDA-F0DF-47DA-B76E-6C477AA61DB1}.Release|x86.Build.0 = Release|Any CPU
315+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
316+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|Any CPU.Build.0 = Debug|Any CPU
317+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|arm64.ActiveCfg = Debug|Any CPU
318+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|arm64.Build.0 = Debug|Any CPU
319+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|x64.ActiveCfg = Debug|Any CPU
320+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|x64.Build.0 = Debug|Any CPU
321+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|x86.ActiveCfg = Debug|Any CPU
322+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Debug|x86.Build.0 = Debug|Any CPU
323+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|Any CPU.ActiveCfg = Release|Any CPU
324+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|Any CPU.Build.0 = Release|Any CPU
325+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|arm64.ActiveCfg = Release|Any CPU
326+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|arm64.Build.0 = Release|Any CPU
327+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|x64.ActiveCfg = Release|Any CPU
328+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|x64.Build.0 = Release|Any CPU
329+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|x86.ActiveCfg = Release|Any CPU
330+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D}.Release|x86.Build.0 = Release|Any CPU
313331
EndGlobalSection
314332
GlobalSection(SolutionProperties) = preSolution
315333
HideSolutionNode = FALSE
@@ -328,6 +346,7 @@ Global
328346
{36317E5D-29E1-4622-AC0A-F2A99C29CE1C} = {B8E0DDAA-1A19-409C-AD36-7B32A997446A}
329347
{B6EFF1A5-63B5-4A05-9B9A-410BA94B92AA} = {B446ED5F-1D77-4709-AFBB-F5CAE585B964}
330348
{C5613BDA-F0DF-47DA-B76E-6C477AA61DB1} = {B8E0DDAA-1A19-409C-AD36-7B32A997446A}
349+
{A648922A-2FC4-4B8D-BD83-BDA4671FF82D} = {B8E0DDAA-1A19-409C-AD36-7B32A997446A}
331350
EndGlobalSection
332351
GlobalSection(ExtensibilityGlobals) = postSolution
333352
SolutionGuid = {E6AF9777-3A1E-4FF6-A531-49CD8D66A293}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net7.0-windows</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<UseWindowsForms>true</UseWindowsForms>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<_SuppressWinFormsTrimError>true</_SuppressWinFormsTrimError>
9+
</PropertyGroup>
10+
11+
<PropertyGroup>
12+
<PublishAot>true</PublishAot>
13+
</PropertyGroup>
14+
15+
</Project>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace HostedWindowsForms;
4+
5+
internal static class LibraryFunctions
6+
{
7+
/// <summary>
8+
/// The main entry point for the application.
9+
/// </summary>
10+
[UnmanagedCallersOnly(EntryPoint = nameof(ShowMessageBox))]
11+
public static void ShowMessageBox()
12+
{
13+
// Application configuration does not work in library project.
14+
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
15+
Application.EnableVisualStyles();
16+
Application.Run(new MainForm());
17+
}
18+
}

samples/HostedWindowsForms/MainForm.Designer.cs

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace HostedWindowsForms
2+
{
3+
public partial class MainForm : Form
4+
{
5+
public MainForm()
6+
{
7+
InitializeComponent();
8+
}
9+
10+
private void buttonPressMe_Click(object sender, EventArgs e)
11+
{
12+
MessageBox.Show("We are inside Winforms and it feels great", "WinForms + NativeAOT");
13+
}
14+
}
15+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<root>
2+
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
3+
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
4+
<xsd:element name="root" msdata:IsDataSet="true">
5+
<xsd:complexType>
6+
<xsd:choice maxOccurs="unbounded">
7+
<xsd:element name="metadata">
8+
<xsd:complexType>
9+
<xsd:sequence>
10+
<xsd:element name="value" type="xsd:string" minOccurs="0" />
11+
</xsd:sequence>
12+
<xsd:attribute name="name" use="required" type="xsd:string" />
13+
<xsd:attribute name="type" type="xsd:string" />
14+
<xsd:attribute name="mimetype" type="xsd:string" />
15+
<xsd:attribute ref="xml:space" />
16+
</xsd:complexType>
17+
</xsd:element>
18+
<xsd:element name="assembly">
19+
<xsd:complexType>
20+
<xsd:attribute name="alias" type="xsd:string" />
21+
<xsd:attribute name="name" type="xsd:string" />
22+
</xsd:complexType>
23+
</xsd:element>
24+
<xsd:element name="data">
25+
<xsd:complexType>
26+
<xsd:sequence>
27+
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
28+
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
29+
</xsd:sequence>
30+
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
31+
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
32+
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
33+
<xsd:attribute ref="xml:space" />
34+
</xsd:complexType>
35+
</xsd:element>
36+
<xsd:element name="resheader">
37+
<xsd:complexType>
38+
<xsd:sequence>
39+
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
40+
</xsd:sequence>
41+
<xsd:attribute name="name" type="xsd:string" use="required" />
42+
</xsd:complexType>
43+
</xsd:element>
44+
</xsd:choice>
45+
</xsd:complexType>
46+
</xsd:element>
47+
</xsd:schema>
48+
<resheader name="resmimetype">
49+
<value>text/microsoft-resx</value>
50+
</resheader>
51+
<resheader name="version">
52+
<value>2.0</value>
53+
</resheader>
54+
<resheader name="reader">
55+
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
56+
</resheader>
57+
<resheader name="writer">
58+
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
59+
</resheader>
60+
</root>

samples/HostedWindowsForms/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
WinForms hosted in DLL
2+
======================
3+
4+
This sample show steps required to put WinForms inside native DLL.
5+
6+
## How to build
7+
8+
Inside `x64 Native Tools Command Prompt for VS 2022` command line run following commands
9+
```
10+
dotnet publish -r win-x64 --self-contained
11+
cl .\loader.c /Z7
12+
```
13+
14+
after that you can run `loader.exe` which will show form.

samples/HostedWindowsForms/loader.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#define PathToLibrary "bin\\Debug\\net7.0-windows\\win-x64\\publish\\HostedWindowsForms.dll"
2+
#define PathToLibrary2 "bin\\x64\\Debug\\net7.0-windows\\win-x64\\publish\\HostedWindowsForms.dll"
3+
4+
#include "windows.h"
5+
#define symLoad GetProcAddress
6+
7+
#include <stdlib.h>
8+
#include <stdio.h>
9+
10+
#ifndef F_OK
11+
#define F_OK 0
12+
#endif
13+
14+
void showMessageBox();
15+
16+
int main()
17+
{
18+
// Check if the library file exists
19+
if (access(PathToLibrary, F_OK) == -1 && access(PathToLibrary2, F_OK) == -1)
20+
{
21+
puts("Couldn't find library at the specified path");
22+
return 0;
23+
}
24+
25+
// Sum two integers
26+
showMessageBox();
27+
}
28+
29+
void showMessageBox()
30+
{
31+
// Call sum function defined in C# shared library
32+
HINSTANCE handle = LoadLibraryA(PathToLibrary);
33+
if (!handle)
34+
{
35+
handle = LoadLibraryA(PathToLibrary2);
36+
}
37+
38+
if (!handle)
39+
{
40+
int errorCode = GetLastError();
41+
printf("Failed to load library at specified path. Error code: %d\n", errorCode);
42+
return;
43+
}
44+
45+
typedef void(*ShowMessageBoxFuncType)();
46+
ShowMessageBoxFuncType ShowMessageBox = (ShowMessageBoxFuncType)symLoad(handle, "ShowMessageBox");
47+
48+
ShowMessageBox();
49+
}

0 commit comments

Comments
 (0)