Document and examples:
You might find these interesting as well.
https://blog.xpnsec.com/rundll32-your-dotnet/ and https://blog.xpnsec.com/the-net-export-portal/
They are not Nim related, but into some good detail about exposing managed code to C-level code. Could be useful for Nim integration.
I tested examples/clr/usage_demo1.nim under I7-8700, 16G, Win10, SSD:
nim r -d:release --cc:tcc -f
Hint: 1184367 lines; 4.903s; 281.684MiB peakmem;
Hint: 1184367 lines; 4.936s; 281.617MiB peakmem;
Hint: 1184367 lines; 4.954s; 281.719MiB peakmem;
All less than 5 secs, hmm, not super quick, but I don't think it is unuseable.
So, if you have a very old computer, you can try following:
I created a gist to explain the steps.
https://gist.github.com/khchen/101aa8c0783ff821216bc7d1608e3896
The result at my computre:
(orginal)
Hint: 1190969 lines; 5.259s; 283.27MiB peakmem;
(minified)
Hint: 145556 lines; 1.674s; 116.773MiB peakmem;
I hope this is useful to you.first I have to confess that I know nothing about .NET programming
can anyone point out how to use external 3rd part .NET DLL? Thanks
for example, the code( from http://cathalscorner.blogspot.com/2010/06/cathal-why-did-you-create-docx.html ) which uses https://github.com/xceedsoftware/DocX
using System;
using Novacode;
using System.Drawing;
namespace DocXHelloWorld
{
class Program
{
static void Main(string[] args)
{
using (DocX document = DocX.Create("Test.docx"))
{
// Add a new Paragraph to the document.
Paragraph p = document.InsertParagraph();
// Append some text.
p.Append("Hello World").Font(new FontFamily("Arial Black"));
// Save the document.
document.Save();
}
}
}
}
can be written in https://github.com/pythonnet/pythonnet as
import clr
clr.AddReference('System.Drawing')
clr.AddReference('Xceed.Words.NET')
import System
import Xceed
document = Xceed.Words.NET.DocX.Create("Test.docx")
p = document.InsertParagraph();
# TypeError: No method matches given arguments for Font: (<class 'System.Drawing.FontFamily'>)
#~ p.Append("Hello World").Font(System.Drawing.FontFamily("Arial Black"));
p.Append("Hello World").Font("Arial Black")
document.Save()
however my nim verison says Error: unhandled exception: variant is not a object (0x80004002) [CLRError] during execution
import winim/clr
var DLL = load("Xceed.Words.NET")
var Words = DLL.new("Xceed.Words.NET")
#~ var document = Words.Create("Test.docx")
var document = Words.DocX.Create("Test.docx")
#~ p = document.InsertParagraph();
#~ p.Append("Hello World").Font("Arial Black")
#~ document.Save()
thanks again
I am DocX user. But I did it in completely VB .Net. Anyhow, I am interested in knowing how to work with .net dlls in Nim and vice versa.
The major problem is that default parameters will not be added into method call in winim/clr. You must specify them by yourself. Look into the source codes of DocX:
public static DocX Create( string filename, DocumentTypes documentType = DocumentTypes.Document )
public override void Save( string password = "" )
Well, Create need an extra parameter documentType, and Save need a password. So, here is the full code that works.
import winim/clr
var
Words = load("Xceed.Words.NET.dll")
Document = load("Xceed.Document.NET.dll")
DocumentTypes = Document.GetType("Xceed.Document.NET.DocumentTypes")
DocX = Words.GetType("Xceed.Words.NET.DocX")
var doc = @DocX.Create("test.docx", @DocumentTypes.Document[DocumentTypes])
# Using 0[DocumentTypes] may be easier, but you must know DocumentTypes.Document is 0
var p = doc.InsertParagraph()
p.Append("Hello World").Font("Arial Black")
doc.Save("")
again, question on create nim variant based on .NET class.
IWorkbook.cs of NPOI says
namespace NPOI.SS.UserModel
{
public interface IWorkbook : ICloseable
{
int ActiveSheetIndex { get; }
int FirstVisibleTab { get; set; }
...
}
}
this post uses it like
IWorkbook workbook;
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
if (fileName.IndexOf(".xlsx") > 0)
workbook = new XSSFWorkbook(fs);
else if (fileName.IndexOf(".xls") > 0)
workbook = new HSSFWorkbook(fs);
//First sheet
so I tried to translated it as following, but failed to declare variant workbook which should be of IWorkbook in C#
var
NPOI = clr.load("NPOI.dll")
NPOIOOXML = clr.load("NPOI.OOXML.dll")
var
File = mscor.GetType("System.IO.File")
HSSFWorkbook = NPOI.GetType("NPOI.HSSF.UserModel.HSSFWorkbook")
XSSFWorkbook = NPOIOOXML.GetType("NPOI.XSSF.UserModel.XSSFWorkbook")
IWorkbook = NPOI.GetType("NPOI.SS.UserModel.IWorkbook")
var fs = @File.OpenRead(fileName);
#~ var workbook: IWorkbook # does not work
#~ var workbook: IWorkbook.GetType() # does not work
#~ var workbook: IWorkbook.GetType # does not work
if fileName.toLower().endsWith(".xlsx") :
workbook = @XSSFWorkbook.new(fs);
elif fileName.toLower().endsWith(".xls"):
workbook = @HSSFWorkbook.new(fs);
Any fix? Thanks.
Btw, var workbook = @WorkbookFactory.Create(fileName) can be used no matter fileName is XLS or XLSX. But the question create nim variant based on .NET class maybe occur in other case.
All clr objects are stored as variant in nim. And it just a wrap of win32 VARIANT .
To distinguish com variant from clr variant, clr.nim defines following types.
type
CLRType = distinct variant
CLRVariant = distinct variant
So, they are all the same in fact. The only difference is that CLRVariant uses instance binding to invoking a member, and CLRType uses static binding. However, all return type of clr operation are CLRVariant. For convenience, @ operator can be used to convert some CLRVariant into CLRType for static binding.
In your example, IWorkbook.GetType() returned an object of .net Type class and stored as CLRVariant in nim, so it cannot be used as type definition. If you want to define some variant before using them, you can use var workbook: CLRVariant. You can also use nim's magic type to see what shoud you use. For example:
proc test(): int = discard
echo test().type # int
var i: test().type
t = test()
echo clr.load("mscorlib").type # CLRVariant
Thank you, Ward.
Yet another question: how to handle C# variant name with special characters? for example
namespace NPOI.OpenXmlFormats.Wordprocessing
{
[XmlType(Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
[Serializable]
public enum ST_TblLayoutType
{
@fixed,
autofit,
}
}
in my code dump @ST_TblLayoutType.autofit shows
@ST_TblLayoutType.autofit = 1
however, I can't write
dump @ST_TblLayoutType.`@fixed`
dump @ST_TblLayoutType.invoke("@fixed")
var flags = BindingFlags_Public
dump @ST_TblLayoutType.invoke("@fixed", flags)
Ignore the @, just use fixed as the name. Moreover, if you want to invoke something by yourself, you have to add some correct binding flag. Like the usage_demo does.
echo @ST_TblLayoutType.fixed
echo @ST_TblLayoutType.invoke("fixed", BindingFlags_GetField or BindingFlags_Static or BindingFlags_Public)
import sugar
import winim/clr
var
mscor* = load("mscorlib")
NPOIOOXML* = clr.load("NPOI.OOXML.dll")
var
File* = mscor.GetType("System.IO.File")
WorkbookFactory* = NPOIOOXML.GetType("NPOI.SS.UserModel.WorkbookFactory")
proc fn(fnExcel: string, strSheet: int|string=0): int =
var stream= @File.OpenRead(fnExcel)
var workbook = @WorkbookFactory.Create(stream);
return workbook.NumberOfSheets
var fnExcel = "testR.xlsx"
dump fn(fnExcel, 0)
#~ dump fn(fnExcel, "Sheet1")
I believed that you found a compiler bug about generic type. You can report it, or use some workarounds:
1. Use except to avoid symbol confliction
import winim/clr except Create
2. Don't use generic type, using type overload instead.
proc fn(fnExcel: string, strSheet: int): int = discard
proc fn(fnExcel: string, strSheet: string): int = discard
thank you for the workaround, I modified my code according to them.
Also, I reported the issues because I am too lazy to write long code later.