Skip to content

Commit 977e651

Browse files
author
Jett Jones
committed
Node Sharp baseline, loading and executing managed dlls
1 parent d24b545 commit 977e651

28 files changed

+1073
-363
lines changed

README.md

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,6 @@
1-
# Creating a nodejs native .Net extension
2-
This short writeup will guide you through creating a simple node.js addon
1+
# Loading .Net libraries in Node Js
2+
This is a native nodejs extension which allows us to load dot-net assemblies, create objects and call methods on them.
33

4-
## Before we begin
5-
1. Read [this](http://coderesearchlabs.com/articles/BNWCA.pdf) short description on how to build a simple nodejs addon on windows.
6-
2. Download node.js [sources](http://nodejs.org/#download).
7-
3. Make sure you have [Python 2.x](http://www.python.org/download/) installed (not 3.x).
8-
4. Build node sources by running `vcbuild.bat'.
9-
5. Verify that node.lib is located under the Debug/ directory.
4+
## Based upon
5+
1. saary's node dotnet and [v8sharp](http://v8sharp.codeplex.com/)
106

11-
## Creating a .Net node addon
12-
1. Fire up VS2010 and create a new C++ Empty CLR project.
13-
![new project](https://github.com/saary/node.net/raw/master/images/CreateClrVCProject.png)
14-
2. Open the project properties (Click Alt+Enter while the project file is selected)
15-
3. Set the project type and extension project type should be *dll* and the extension should be *.node*
16-
![new project](https://github.com/saary/node.net/raw/master/images/SetProjectType.png)
17-
4. Set the include directories as follows (replace paths with the local nodejs source path).
18-
![new project](https://github.com/saary/node.net/raw/master/images/SetIncludes.png)
19-
5. Set the libraries directories as follows (replace paths with the local nodejs source path).
20-
![new project](https://github.com/saary/node.net/raw/master/images/SetLibrariesDirs.png)
21-
6. Add a new CPP file and call it SOME_PREFIX_Addon.cpp.
22-
This file will hold the actual addon definitions and dll entry point.
23-
7. Open the property pages of the **new CPP file** you created.
24-
8. Change the "Common Language Runtime Support" option to **No Common Language RunTime Support**
25-
![new project](https://github.com/saary/node.net/raw/master/images/AddonProperties.png)
26-
27-
## What's next?
28-
* use the project provided here as an initial implementation.
29-
* take a look at the following [html presentation](http://kkaefer.github.com/node-cpp-modules/#api-how-hard-to-misuse).
30-
the ppt explains the different steps necessary to register a class with V8.
31-
* start rocking!

Sharp.sln

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 11.00
33
# Visual Studio 2010
44
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Sharp", "Sharp\Sharp.vcxproj", "{74153EEB-90DB-4E5B-AA58-2AD21AEDD3CD}"
55
EndProject
6-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpLib", "SharpLib\SharpLib.csproj", "{17CEA729-1E55-4E47-BD12-1FDE7B23124E}"
7-
EndProject
86
Global
97
GlobalSection(SolutionConfigurationPlatforms) = preSolution
108
Debug|Any CPU = Debug|Any CPU
@@ -25,16 +23,6 @@ Global
2523
{74153EEB-90DB-4E5B-AA58-2AD21AEDD3CD}.Release|Mixed Platforms.Build.0 = Release|Win32
2624
{74153EEB-90DB-4E5B-AA58-2AD21AEDD3CD}.Release|Win32.ActiveCfg = Release|Win32
2725
{74153EEB-90DB-4E5B-AA58-2AD21AEDD3CD}.Release|Win32.Build.0 = Release|Win32
28-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Debug|Any CPU.Build.0 = Debug|Any CPU
30-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
31-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
32-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Debug|Win32.ActiveCfg = Debug|Any CPU
33-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Release|Any CPU.ActiveCfg = Release|Any CPU
34-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Release|Any CPU.Build.0 = Release|Any CPU
35-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
36-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
37-
{17CEA729-1E55-4E47-BD12-1FDE7B23124E}.Release|Win32.ActiveCfg = Release|Any CPU
3826
EndGlobalSection
3927
GlobalSection(SolutionProperties) = preSolution
4028
HideSolutionNode = FALSE

Sharp/MatchType.cpp

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#include "MatchType.h"
2+
3+
using namespace System;
4+
using namespace System::Reflection;
5+
using namespace System::Collections::Generic;
6+
7+
8+
Object^ CreateObject(Dictionary<String^, Object^>^ dictionary, Type^ type)
9+
{
10+
ConstructorInfo^ constructor = type->GetConstructor(gcnew array<Type^>{});
11+
12+
if (constructor == nullptr)
13+
{
14+
throw gcnew Exception("Constructing objects requires a zero arg constructor.");
15+
}
16+
17+
Object^ o = constructor->Invoke(gcnew array<Object^>{});
18+
19+
for each(String^ key in dictionary->Keys)
20+
{
21+
Object^ value = dictionary[key];
22+
23+
// find a field
24+
FieldInfo^ f = type->GetField(key);
25+
PropertyInfo^ p = type->GetProperty(key);
26+
27+
if (f != nullptr)
28+
{
29+
Object^ adjusted = MatchType::AdjustObject(value, f->FieldType);
30+
f->SetValue(o, adjusted);
31+
}
32+
else if (p != nullptr)
33+
{
34+
p->SetValue(o, MatchType::AdjustObject(value, p->PropertyType), nullptr);
35+
}
36+
else
37+
{
38+
throw gcnew Exception(String::Format("No matching field found for {0} on {1}", key, type->Name));
39+
}
40+
}
41+
42+
return o;
43+
}
44+
45+
Object^ CreateArray(Array^ input, Type^ t)
46+
{
47+
Type^ arrayType = t->GetElementType();
48+
ConstructorInfo^ c = t->GetConstructor(gcnew array<Type^> {int::typeid});
49+
50+
if (c == nullptr)
51+
{
52+
throw gcnew Exception(String::Format("Failed to find constructor for array type {0}", t));
53+
}
54+
55+
Array^ result = (Array^)c->Invoke(gcnew array<Object^> {input->Length});
56+
57+
int index = 0;
58+
for each(Object^ item in input)
59+
{
60+
Object^ converted = MatchType::AdjustObject(item, arrayType);
61+
result->SetValue(converted, index++);
62+
}
63+
64+
return result;
65+
}
66+
67+
Object^ CreateList(Array^ input, Type^ t)
68+
{
69+
Type^ listType = t->GetGenericArguments()[0];
70+
ConstructorInfo^ c = t->GetConstructor(gcnew array<Type^>{int::typeid});
71+
72+
if (c == nullptr)
73+
{
74+
throw gcnew Exception(String::Format("Failed to find constructor for array type {0}", t));
75+
}
76+
77+
Object^ result = c->Invoke(gcnew array<Object^> {input->Length});
78+
79+
MethodInfo^ addMethod = t->GetMethod("Add");
80+
81+
for each (Object^ item in input)
82+
{
83+
Object^ converted = MatchType::AdjustObject(item, listType);
84+
addMethod->Invoke(result, gcnew array<Object^>{converted});
85+
}
86+
87+
return result;
88+
}
89+
90+
array<Object^>^ MatchType::AdjustArguments(array<Object^>^ paramList, array<ParameterInfo^>^ paramTypes)
91+
{
92+
List<Object^>^ result = gcnew List<Object^>();
93+
for (int i = 0; i < paramTypes->Length; i++ )
94+
{
95+
Object^ obj = (paramList->Length > i) ? paramList[i] : nullptr;
96+
Type^ paramType = paramTypes[i]->ParameterType;
97+
Object^ adjusted = MatchType::AdjustObject(obj, paramType);
98+
99+
result->Add(adjusted);
100+
}
101+
102+
return result->ToArray();
103+
}
104+
105+
Object^ MatchType::AdjustObject(Object^ obj, Type^ paramType)
106+
{
107+
static gcroot<Type^> DictionaryType = Dictionary<String^, Object^>::typeid;
108+
static gcroot<Type^> GenericList = List<int>::typeid->GetGenericTypeDefinition();
109+
if (obj == nullptr){ return nullptr; }
110+
111+
Type^ t = obj->GetType();
112+
113+
if (paramType->IsAssignableFrom(t))
114+
{
115+
// aready good.
116+
return obj;
117+
}
118+
119+
// case 1 - generated a dictionary.
120+
if (t->IsAssignableFrom(DictionaryType))
121+
{
122+
return CreateObject(safe_cast<Dictionary<String^, Object^>^>(obj), paramType);
123+
}
124+
125+
// case 2 - generated an array
126+
if (t->IsArray)
127+
{
128+
if (paramType->IsArray)
129+
{
130+
return CreateArray(safe_cast<Array^>(obj), paramType);
131+
}
132+
133+
if (paramType->IsGenericType && paramType->GetGenericTypeDefinition()->IsAssignableFrom(GenericList))
134+
{
135+
// assigning to a list.
136+
return CreateList(safe_cast<Array^>(obj), paramType);
137+
}
138+
139+
// some more complicated vector? give up
140+
throw gcnew Exception(String::Format("Could not convert array types {0} to {1}", t, paramType));
141+
}
142+
143+
// couldn't figure out a good conversion, give up.
144+
return obj;
145+
}
146+

Sharp/MatchType.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
3+
#include <vcclr.h>
4+
5+
class MatchType
6+
{
7+
public:
8+
static array<System::Object^>^ AdjustArguments(array<System::Object^>^ argumentList, array<System::Reflection::ParameterInfo^>^ types);
9+
10+
static System::Object^ AdjustObject(System::Object^ obj, System::Type^ type);
11+
};

Sharp/Sharp.vcxproj

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@
4141
<PropertyGroup Label="UserMacros" />
4242
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
4343
<LinkIncremental>true</LinkIncremental>
44-
<IncludePath>E:\node-v0.6.2\deps\v8\include;E:\node-v0.6.2\deps\uv\include;E:\node-v0.6.2\src;$(IncludePath)</IncludePath>
45-
<LibraryPath>E:\node-v0.6.2\Debug;E:\node-v0.6.2\Debug\lib;$(LibraryPath)</LibraryPath>
44+
<IncludePath>C:\node-v0.6.6\deps\v8\include;c:\node-v0.6.6\deps\uv\include;c:\node-v0.6.6\src;$(IncludePath)</IncludePath>
45+
<LibraryPath>C:\node-v0.6.6\Debug;C:\node-v0.6.6\Debug\lib;$(LibraryPath)</LibraryPath>
4646
<TargetExt>.node</TargetExt>
47-
<ReferencePath>E:\Public Projects\node.net\SharpLib\bin\Debug;$(ReferencePath)</ReferencePath>
47+
<ReferencePath>$(ReferencePath)</ReferencePath>
4848
</PropertyGroup>
4949
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
5050
<LinkIncremental>false</LinkIncremental>
@@ -72,19 +72,34 @@
7272
</Link>
7373
</ItemDefinitionGroup>
7474
<ItemGroup>
75+
<ClCompile Include="MatchType.cpp" />
7576
<ClCompile Include="SharpAddon.cpp">
7677
<CompileAsManaged Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</CompileAsManaged>
7778
</ClCompile>
7879
<ClCompile Include="SharpLibHelper.cpp" />
7980
<ClCompile Include="v8external.cpp" />
8081
<ClCompile Include="v8function.cpp" />
8182
<ClCompile Include="v8value.cpp" />
83+
<ClCompile Include="v8wrap.cpp" />
84+
<ClCompile Include="WrapAssembly.cpp" />
85+
<ClCompile Include="WrapBase.cpp" />
86+
<ClCompile Include="WrapInstance.cpp" />
8287
</ItemGroup>
8388
<ItemGroup>
89+
<ClInclude Include="MatchType.h" />
8490
<ClInclude Include="SharpLibHelper.h" />
8591
<ClInclude Include="v8external.h" />
8692
<ClInclude Include="v8function.h" />
8793
<ClInclude Include="v8value.h" />
94+
<ClInclude Include="v8wrap.h" />
95+
<ClInclude Include="WrapAssembly.h" />
96+
<ClInclude Include="WrapBase.h" />
97+
<ClInclude Include="WrapInstance.h" />
98+
</ItemGroup>
99+
<ItemGroup>
100+
<None Include="app.config">
101+
<SubType>Designer</SubType>
102+
</None>
88103
</ItemGroup>
89104
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
90105
<ImportGroup Label="ExtensionTargets">

Sharp/Sharp.vcxproj.filters

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<ItemGroup>
4+
<ClCompile Include="SharpAddon.cpp" />
5+
<ClCompile Include="v8external.cpp">
6+
<Filter>v8sharp</Filter>
7+
</ClCompile>
8+
<ClCompile Include="v8function.cpp">
9+
<Filter>v8sharp</Filter>
10+
</ClCompile>
11+
<ClCompile Include="v8value.cpp">
12+
<Filter>v8sharp</Filter>
13+
</ClCompile>
14+
<ClCompile Include="v8wrap.cpp">
15+
<Filter>v8sharp</Filter>
16+
</ClCompile>
17+
<ClCompile Include="WrapAssembly.cpp">
18+
<Filter>wrap</Filter>
19+
</ClCompile>
20+
<ClCompile Include="WrapInstance.cpp">
21+
<Filter>wrap</Filter>
22+
</ClCompile>
23+
<ClCompile Include="MatchType.cpp">
24+
<Filter>wrap</Filter>
25+
</ClCompile>
26+
<ClCompile Include="SharpLibHelper.cpp" />
27+
<ClCompile Include="WrapBase.cpp">
28+
<Filter>wrap</Filter>
29+
</ClCompile>
30+
</ItemGroup>
31+
<ItemGroup>
32+
<ClInclude Include="v8wrap.h">
33+
<Filter>v8sharp</Filter>
34+
</ClInclude>
35+
<ClInclude Include="v8external.h">
36+
<Filter>v8sharp</Filter>
37+
</ClInclude>
38+
<ClInclude Include="v8function.h">
39+
<Filter>v8sharp</Filter>
40+
</ClInclude>
41+
<ClInclude Include="v8value.h">
42+
<Filter>v8sharp</Filter>
43+
</ClInclude>
44+
<ClInclude Include="WrapAssembly.h">
45+
<Filter>wrap</Filter>
46+
</ClInclude>
47+
<ClInclude Include="WrapInstance.h">
48+
<Filter>wrap</Filter>
49+
</ClInclude>
50+
<ClInclude Include="MatchType.h">
51+
<Filter>wrap</Filter>
52+
</ClInclude>
53+
<ClInclude Include="SharpLibHelper.h" />
54+
<ClInclude Include="WrapBase.h">
55+
<Filter>wrap</Filter>
56+
</ClInclude>
57+
</ItemGroup>
58+
<ItemGroup>
59+
<None Include="app.config" />
60+
</ItemGroup>
61+
<ItemGroup>
62+
<Filter Include="v8sharp">
63+
<UniqueIdentifier>{f19d441d-6676-4dcd-b8b1-f818a6dd9cfe}</UniqueIdentifier>
64+
</Filter>
65+
<Filter Include="wrap">
66+
<UniqueIdentifier>{6521fba0-c33c-4757-b81a-0cb2d36ea3af}</UniqueIdentifier>
67+
</Filter>
68+
</ItemGroup>
69+
</Project>

Sharp/Sharp.vcxproj.user

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
4+
<LocalDebuggerCommand>C:\Program Files\nodejs\node.exe</LocalDebuggerCommand>
5+
</PropertyGroup>
6+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
7+
<LocalDebuggerWorkingDirectory>..\Debug</LocalDebuggerWorkingDirectory>
8+
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
9+
</PropertyGroup>
10+
</Project>

0 commit comments

Comments
 (0)