Skip to content

Commit 74e178c

Browse files
author
Barton Cline
committed
# Visual Studio 2010 .NET 4.0 (with a little ReSharper thrown in) #
# The new C# clrmodule depends on RGiesecke.DllExport in new pythonnet/packages directory which was import via NuGet # # The signing key finally landed in the correct directory # # Not sure why the console/Console.csproj referenced Python.Test # # Python.Runtime Assembly version bumped to 4.0.0.1 # # Tool and Framework versions all bumped for .NET 4.0 # # [Obsolete?] buildclrmodule.bat got a tiny note after cli research on clrmodule.il # # EmbeddingTest remains in flux while issues with nUnit (version bump) get ironed out #
1 parent 53b6f30 commit 74e178c

24 files changed

+489
-31
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0"?>
2+
<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
3+
<metadata>
4+
<id>UnmanagedExports</id>
5+
<version>1.2.3-Beta</version>
6+
<title>Unmanaged Exports (DllExport for .Net)</title>
7+
<authors>Robert Giesecke</authors>
8+
<owners>Robert Giesecke</owners>
9+
<licenseUrl>http://opensource.org/licenses/mit-license.php</licenseUrl>
10+
<projectUrl>https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports</projectUrl>
11+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
12+
<description>A set of compile-time libraries (nothing to deploy) and a build task that enable you to export functions from managed code to native applications.
13+
That means, you can create plugins in a managed language like C# or F# for native applications that only have a C-Api (like Notepad++).
14+
The nuget package is all you need. Just mark your methods with [DllExport] and build.
15+
16+
Hints:
17+
- You have to set your platform target to either x86, ia64 or x64. AnyCPU assemblies cannot export functions.
18+
- The export name defaults to the method name and the calling convention to stdcall. If that's all what you want, you can just use [DllExport] without parameters.
19+
- You cannot put your exports in generic types or export gegenric methods, but your parameters or the result can use generics.
20+
e.g.:
21+
[DllExport]
22+
static void Test(YourStruct&lt;int&gt; data){}</description>
23+
<summary>Adds the ability to declare unmanaged function exports.
24+
IOW: the exact opposite of how DllImport works.</summary>
25+
<releaseNotes>- Placing [DllExport] on non-static methods will now yield an error.
26+
- Placing [DllExport] on methods in generic types (or types nested in generic types) will yield an error.
27+
- Errors regarding lib.exe will no longer break the build.
28+
- Added support for generic return types. Even the most esotheric corner cases (obfuscated types) should work.
29+
- Made the code that checks method names and class names much more resilient and faster.
30+
- Most messages are localizable now (and I translated them to German).</releaseNotes>
31+
<language>en-US</language>
32+
<tags>dllexport unmanaged exports export</tags>
33+
<references>
34+
<reference file="RGiesecke.DllExport.Metadata.dll" />
35+
</references>
36+
</metadata>
37+
</package>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
function Remove-OldDllExportFolder
2+
{
3+
param($project)
4+
$defaultFiles = ('DllExportAttribute.cs',
5+
'Mono.Cecil.dll',
6+
'RGiesecke.DllExport.dll',
7+
'RGiesecke.DllExport.pdb',
8+
'RGiesecke.DllExport.MSBuild.dll',
9+
'RGiesecke.DllExport.MSBuild.pdb',
10+
'RGiesecke.DllExport.targets')
11+
12+
$projectFile = New-Object IO.FileInfo($project.FullName)
13+
14+
$projectFile.Directory.GetDirectories("DllExport") | Select-Object -First 1 | % {
15+
$dllExportDir = $_
16+
17+
if($dllExportDir.GetDirectories().Count -eq 0){
18+
$unknownFiles = $dllExportDir.GetFiles() | Select -ExpandProperty Name | ? { -not $defaultFiles -contains $_ }
19+
20+
if(-not $unknownFiles){
21+
Write-Host "Removing 'DllExport' from " $project.Name
22+
$project.ProjectItems | ? { $_.Name -eq 'DllExport' } | % {
23+
$_.Remove()
24+
}
25+
26+
Write-Host "Deleting " $dllExportDir.FullName " ..."
27+
$dllExportDir.Delete($true)
28+
}
29+
}
30+
}
31+
}
32+
33+
function Remove-OldDllExportFolders
34+
{
35+
Get-Project -all | % {
36+
Remove-OldDllExportFolder $_
37+
}
38+
}
39+
40+
Export-ModuleMember Remove-OldDllExportFolder
41+
Export-ModuleMember Remove-OldDllExportFolders
Binary file not shown.
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
4+
<PropertyGroup>
5+
<PostBuildEventDependsOn>
6+
$(PostBuildEventDependsOn);
7+
RGieseckeDllExport
8+
</PostBuildEventDependsOn>
9+
</PropertyGroup>
10+
11+
<PropertyGroup>
12+
<BuildDependsOn>
13+
$(BuildDependsOn);
14+
RGieseckeDllExport
15+
</BuildDependsOn>
16+
</PropertyGroup>
17+
18+
<UsingTask TaskName="RGiesecke.DllExport.MSBuild.DllExportAppDomainIsolatedTask"
19+
AssemblyFile="RGiesecke.DllExport.MSBuild.dll" />
20+
21+
<Target Name="RGieseckeDllExport"
22+
DependsOnTargets="GetFrameworkPaths">
23+
24+
<!--
25+
These properties can still be applied to the task, but upon installation of a
26+
new version of the nuget package, the properties
27+
DllExportAttributeAssemblyName and DllExportAttributeAssemblyName will be removed from the project.
28+
So, if you want to provide an alternative attribute name, the you have to name the property in your project file differently.
29+
30+
e.g.:
31+
DllExportAttributeAssemblyName="$(MyDllExportAttributeAssemblyName)"
32+
DllExportAttributeFullName="$(MyDllExportAttributeFullName)"
33+
-->
34+
35+
36+
<DllExportAppDomainIsolatedTask Platform="$(Platform)"
37+
PlatformTarget="$(PlatformTarget)"
38+
CpuType="$(CpuType)"
39+
EmitDebugSymbols="$(DebugSymbols)"
40+
Timeout="$(DllExportTimeout)"
41+
KeyContainer="$(KeyContainerName)$(AssemblyKeyContainerName)"
42+
KeyFile="$(KeyOriginatorFile)"
43+
ProjectDirectory="$(MSBuildProjectDirectory)"
44+
InputFileName="$(TargetPath)"
45+
FrameworkPath="$(TargetedFrameworkDir);$(TargetFrameworkDirectory)"
46+
LibToolPath="$(DevEnvDir)\..\..\VC\bin"
47+
LibToolDllPath="$(DevEnvDir)"
48+
TargetFrameworkVersion="$(TargetFrameworkVersion)"
49+
SdkPath="$(TargetFrameworkSDKToolsDirectory)"
50+
SkipOnAnyCpu="$(NoDllExportsForAnyCpu)"/>
51+
</Target>
52+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
param($installPath, $toolsPath, $package, $project)
2+
3+
Import-Module (Join-Path $toolsPath DllExportCmdLets.psm1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
param($installPath, $toolsPath, $package, $project)
2+
3+
$targetFileName = 'RGiesecke.DllExport.targets'
4+
$targetFileName = [IO.Path]::Combine($toolsPath, $targetFileName)
5+
$targetUri = New-Object Uri -ArgumentList $targetFileName, [UriKind]::Absolute
6+
7+
Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
8+
9+
# change the reference to RGiesecke.DllExport.Metadata.dll to not be copied locally
10+
11+
$project.Object.References | ? {
12+
$_.Name -eq "RGiesecke.DllExport.Metadata"
13+
} | % {
14+
if($_ | Get-Member | ? {$_.Name -eq "CopyLocal"}){
15+
$_.CopyLocal = $false
16+
}
17+
}
18+
19+
$projects = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName)
20+
$projects | % {
21+
$currentProject = $_
22+
23+
# remove imports of RGiesecke.DllExport.targets from this project
24+
$currentProject.Xml.Imports | ? {
25+
return ("RGiesecke.DllExport.targets" -eq [IO.Path]::GetFileName($_.Project))
26+
} | % {
27+
$currentProject.Xml.RemoveChild($_);
28+
}
29+
30+
# remove the properties DllExportAttributeFullName and DllExportAttributeAssemblyName
31+
$currentProject.Xml.Properties | ? {
32+
$_.Name -eq "DllExportAttributeFullName" -or $_.Name -eq "DllExportAttributeAssemblyName"
33+
} | % {
34+
$_.Parent.RemoveChild($_)
35+
}
36+
37+
$projectUri = New-Object Uri -ArgumentList $currentProject.FullPath, [UriKind]::Absolute
38+
$relativeUrl = $projectUri.MakeRelative($targetUri)
39+
[Void]$currentProject.Xml.AddImport($relativeUrl)
40+
41+
# remove the old stuff in the DllExports folder from previous versions, (will check that only known files are in it)
42+
Remove-OldDllExportFolder $project
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
param($installPath, $toolsPath, $package, $project)
2+
3+
$targetFileName = 'RGiesecke.DllExport.targets'
4+
$targetFileName = [System.IO.Path]::Combine($toolsPath, $targetFileName)
5+
$targetUri = New-Object Uri -ArgumentList $targetFileName, [UriKind]::Absolute
6+
7+
Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
8+
9+
$projects = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName)
10+
11+
return $projects | % {
12+
$currentProject = $_
13+
14+
$currentProject.Xml.Imports | ? {
15+
return ("RGiesecke.DllExport.targets" -eq [System.IO.Path]::GetFileName($_.Project))
16+
} | % {
17+
$currentProject.Xml.RemoveChild($_)
18+
}
19+
}
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<repositories>
3+
<repository path="..\clrmodule\packages.config" />
4+
<repository path="..\src\runtime\packages.config" />
5+
</repositories>

pythonnet/pythonnet.sln

+14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.EmbeddingTest", "src
99
EndProject
1010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "src\console\Console.csproj", "{E29DCF0A-5114-4A98-B1DD-71264B6EA349}"
1111
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "clrmodule", "src\clrmodule\clrmodule.csproj", "{86E834DE-1139-4511-96CC-69636A56E7AC}"
13+
EndProject
1214
Global
1315
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1416
Debug|Any CPU = Debug|Any CPU
@@ -85,6 +87,18 @@ Global
8587
{E29DCF0A-5114-4A98-B1DD-71264B6EA349}.UnitTests|Any CPU.Build.0 = UnitTests|Any CPU
8688
{E29DCF0A-5114-4A98-B1DD-71264B6EA349}.UnitTests|x64.ActiveCfg = UnitTests|Any CPU
8789
{E29DCF0A-5114-4A98-B1DD-71264B6EA349}.UnitTests|x64.Build.0 = UnitTests|Any CPU
90+
{86E834DE-1139-4511-96CC-69636A56E7AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
91+
{86E834DE-1139-4511-96CC-69636A56E7AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
92+
{86E834DE-1139-4511-96CC-69636A56E7AC}.Debug|x64.ActiveCfg = Debug|Any CPU
93+
{86E834DE-1139-4511-96CC-69636A56E7AC}.EmbeddingTest|Any CPU.ActiveCfg = Release|Any CPU
94+
{86E834DE-1139-4511-96CC-69636A56E7AC}.EmbeddingTest|Any CPU.Build.0 = Release|Any CPU
95+
{86E834DE-1139-4511-96CC-69636A56E7AC}.EmbeddingTest|x64.ActiveCfg = Release|Any CPU
96+
{86E834DE-1139-4511-96CC-69636A56E7AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
97+
{86E834DE-1139-4511-96CC-69636A56E7AC}.Release|Any CPU.Build.0 = Release|Any CPU
98+
{86E834DE-1139-4511-96CC-69636A56E7AC}.Release|x64.ActiveCfg = Release|Any CPU
99+
{86E834DE-1139-4511-96CC-69636A56E7AC}.UnitTests|Any CPU.ActiveCfg = Release|Any CPU
100+
{86E834DE-1139-4511-96CC-69636A56E7AC}.UnitTests|Any CPU.Build.0 = Release|Any CPU
101+
{86E834DE-1139-4511-96CC-69636A56E7AC}.UnitTests|x64.ActiveCfg = Release|Any CPU
88102
EndGlobalSection
89103
GlobalSection(SolutionProperties) = preSolution
90104
HideSolutionNode = FALSE
File renamed without changes.

pythonnet/src/clrmodule/ClrModule.cs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// ==========================================================================
2+
// This software is subject to the provisions of the Zope Public License,
3+
// Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
4+
// THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
5+
// WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
6+
// WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
7+
// FOR A PARTICULAR PURPOSE.
8+
// ==========================================================================
9+
10+
//============================================================================
11+
// This file replaces the hand-maintained stub that used to implement clr.dll.
12+
// This is a line-by-line port from IL back to C#.
13+
// We now use RGiesecke.DllExport on the required static init method so it can be
14+
// loaded by a standard CPython interpreter as an extension module. When it
15+
// is loaded, it bootstraps the managed runtime integration layer and defers
16+
// to it to do initialization and put the clr module into sys.modules, etc.
17+
18+
// The "USE_PYTHON_RUNTIME_*" defines control what extra evidence is used
19+
// to help the CLR find the appropriate Python.Runtime assembly.
20+
21+
// If defined, the "pythonRuntimeVersionString" variable must be set to
22+
// Python.Runtime's current version.
23+
24+
#define USE_PYTHON_RUNTIME_VERSION
25+
26+
// If defined, the "PythonRuntimePublicKeyTokenData" data array must be
27+
// set to Python.Runtime's public key token. (sn -T Python.Runtin.dll)
28+
#define USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN
29+
30+
// If DEBUG_PRINT is defined, a few System.Console.WriteLine calls are made
31+
// to indicate what's going on during the load...
32+
#define DEBUG_PRINT
33+
//============================================================================
34+
35+
36+
// ReSharper disable CheckNamespace
37+
// ReSharper disable InconsistentNaming
38+
public class clrModule
39+
// ReSharper restore InconsistentNaming
40+
// ReSharper restore CheckNamespace
41+
{
42+
43+
[RGiesecke.DllExport.DllExport("initclr", System.Runtime.InteropServices.CallingConvention.StdCall)]
44+
// ReSharper disable InconsistentNaming
45+
public static void initclr()
46+
// ReSharper restore InconsistentNaming
47+
{
48+
#if DEBUG_PRINT
49+
System.Console.WriteLine("Attempting to load Python.Runtime using standard binding rules... ");
50+
#endif
51+
#if USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN
52+
var pythonRuntimePublicKeyTokenData = new byte[] { 0x50, 0x00, 0xfe, 0xa6, 0xcb, 0xa7, 0x02, 0xdd };
53+
#endif
54+
55+
// Attempt to find and load Python.Runtime using standard assembly binding rules.
56+
// This roughly translates into looking in order:
57+
// - GAC
58+
// - ApplicationBase
59+
// - A PrivateBinPath under ApplicationBase
60+
// With an unsigned assembly, the GAC is skipped.
61+
var pythonRuntimeName = new System.Reflection.AssemblyName("Python.Runtime")
62+
{
63+
#if USE_PYTHON_RUNTIME_VERSION
64+
Version = new System.Version("4.0.0.1"),
65+
#endif
66+
CultureInfo = System.Globalization.CultureInfo.InvariantCulture,
67+
};
68+
#if USE_PYTHON_RUNTIME_PUBLIC_KEY_TOKEN
69+
pythonRuntimeName.SetPublicKeyToken(pythonRuntimePublicKeyTokenData);
70+
#endif
71+
// We've got the AssemblyName with optional features; try to load it.
72+
System.Reflection.Assembly pythonRuntime;
73+
try
74+
{
75+
pythonRuntime = System.Reflection.Assembly.Load(pythonRuntimeName);
76+
#if DEBUG_PRINT
77+
System.Console.WriteLine("Success!");
78+
#endif
79+
}
80+
catch (System.IO.FileNotFoundException)
81+
{
82+
try
83+
{
84+
// If the above fails for any reason, we fallback to attempting to load "Python.Runtime.dll"
85+
// from the directory this assembly is running in. "This assembly" is probably "clr.pyd",
86+
// sitting somewhere in PYTHONPATH. This is using Assembly.LoadFrom, and inherits all the
87+
// caveats of that call. See MSDN docs for details.
88+
// Suzanne Cook's blog is also an excellent source of info on this:
89+
// http://blogs.msdn.com/suzcook/
90+
// http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx
91+
// http://blogs.msdn.com/suzcook/archive/2003/06/13/57180.aspx
92+
93+
var executingAssembly = System.Reflection.Assembly.GetExecutingAssembly();
94+
var assemblyDirectory = System.IO.Path.GetDirectoryName(executingAssembly.Location);
95+
if (assemblyDirectory == null)
96+
throw new System.InvalidOperationException(executingAssembly.Location);
97+
var pythonRuntimeDllPath = System.IO.Path.Combine(assemblyDirectory, "Python.Runtime.dll");
98+
#if DEBUG_PRINT
99+
System.Console.WriteLine("Attempting to load Python.Runtime from: '{0}'...", pythonRuntimeDllPath);
100+
#endif
101+
pythonRuntime = System.Reflection.Assembly.LoadFrom(pythonRuntimeDllPath);
102+
}
103+
catch (System.InvalidOperationException) {
104+
#if DEBUG_PRINT
105+
System.Console.WriteLine("Could not load Python.Runtime, so sad.");
106+
#endif
107+
return;
108+
}
109+
}
110+
111+
// Once here, we've successfully loaded SOME version of Python.Runtime
112+
// So now we get the PythonEngine and execute the InitExt method on it.
113+
var pythonEngineType = pythonRuntime.GetType("Python.Runtime.PythonEngine");
114+
pythonEngineType.InvokeMember("InitExt", System.Reflection.BindingFlags.InvokeMethod, null, null, null);
115+
}
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyTitle("clrmodule")]
9+
[assembly: AssemblyDescription("")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("clrmodule")]
13+
[assembly: AssemblyCopyright("Copyright © 2013")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// Setting ComVisible to false makes the types in this assembly not visible
18+
// to COM components. If you need to access a type in this assembly from
19+
// COM, set the ComVisible attribute to true on that type.
20+
[assembly: ComVisible(false)]
21+
22+
// The following GUID is for the ID of the typelib if this project is exposed to COM
23+
[assembly: Guid("ae10d6a4-55c2-482f-9716-9988e6c169e3")]
24+
25+
// Version information for an assembly consists of the following four values:
26+
//
27+
// Major Version
28+
// Minor Version
29+
// Build Number
30+
// Revision
31+
//
32+
// You can specify all the values or you can default the Build and Revision Numbers
33+
// by using the '*' as shown below:
34+
// [assembly: AssemblyVersion("1.0.*")]
35+
[assembly: AssemblyVersion("1.0.0.0")]
36+
[assembly: AssemblyFileVersion("1.0.0.0")]

0 commit comments

Comments
 (0)