How to use c# class in Excel VBA? - c#

I have a standalone c# applications that does something specific (listens to TCP port and pronounces all strings that arrive to it via speech synthesizer). How can I make the c# class visible to a VBA program, same way other "References" are visible to it? I would appreciate short and clean example. I struggle to find one of those for some reason.
If there are some gotchas specific to c# <-> vba interaction, I would like to know about those too.
Here is a C# code. I build is as a class library with "Register for COM interop" setting.
When I add the resulting .tlb file to VBA references list, I expect to see SayCom library that has SayCom class with 2 methods, square and getCount. I do not see that. What am I missing?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
[assembly: CLSCompliant(true)]
namespace SayCom
{
[CLSCompliant(true)]
[ComVisible(true)]
public class SayCom
{
int count;
public SayCom()
{
count = 0;
}
[ComVisible(true)]
public int square(int x)
{
++count;
return x * x;
}
[ComVisible(true)]
public int getCount()
{
return count;
}
}
}

This works for me
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
[Guid("BBF87E31-77E2-46B6-8093-1689A144BFC6")]
[ComVisible(true)]
public interface MyMiniSubs
{
int square(int x);
int getCount();
}
[Guid("BBF87E31-77E2-46B6-8093-1689A144BFC7")]
[ClassInterface(ClassInterfaceType.None)]
public class Class1 : MyMiniSubs
{
int count;
public Class1()
{
count = 0;
}
[ComVisible(true)]
public int square(int x)
{
++count;
return x * x;
}
[ComVisible(true)]
public int getCount()
{
return count;
}
}
}

The only 'gotcha', as far as I know, is that your C# code needs to be CLS compliant. You should be able to follow the instructions here to import your C# code as a .dll into your VB application. Disclaimer: I have not tried this myself.
EDIT: Here's a more official MSDN reference that talks about calling functions from a .dll.

Related

mono_class_from_name_case returns nullptr

mono_class_from_name_case returns nullptr and i found no way to get more information why, and the mono documentation is very basic. My question is, i think i am missing something, is there a method to "link" the two assemblys together?, is that even needed?, why is the class showing up in the metadata but not accessable via get class?
I get the name and namespace through the metadata interface and they are valid strings. The dlls are compiled without any warnings/errors.
Beetween the open of the assambly and the metadata access is no other operation.
The code is first executed for base.dll then for content.dll. Both are in the same Domain, but different Assemblys and images.
Things i noticed:
Removing the base class in Slime "fixes" it. <- maybe missing a reference to base.dll?
It still does not work, when making the base not abstract/without any virtual method
Changing from mono_class_from_name_case to mono_class_from_name has the same behavior.
C++ Code:
//metadata.getRaw returns a MonoImage* loaded with mono_domain_assembly_open & mono_assembly_get_image, the base dll is loaded first
const MonoTableInfo* table_info = mono_image_get_table_info(metadata.GetRaw(), MONO_TABLE_TYPEDEF);
int rows = mono_table_info_get_rows(table_info);
for (int i = 0; i < rows; i++)
{
MonoClass* _class = nullptr;
uint32_t cols[MONO_TYPEDEF_SIZE];
mono_metadata_decode_row(table_info, i, cols, MONO_TYPEDEF_SIZE);
const char* name = mono_metadata_string_heap(metadata.GetRaw(), cols[MONO_TYPEDEF_NAME]);
const char* name_space = mono_metadata_string_heap(metadata.GetRaw(), cols[MONO_TYPEDEF_NAMESPACE]);
//this returns nullptr for a valid class, i think the base dll is not loaded
_class = mono_class_from_name_case(metadata.GetRaw(), name_space, name);
//this does happen...
if (_class == nullptr) {
continue;
}
}
The two involved DLLs:
Base.dll
csc.exe -target:library -nologo -optimize+ -debug- -out:Base.dll Item.cs
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Base
{
public abstract class Item
{
public Item()
{
}
public abstract bool init();
public abstract void release();
}
}
Content.dll
csc.exe -target:library -nologo -optimize+ -debug- -reference:Base.dll -out:Content.dll Slime.cs
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Content{
public class Slime : Base.Item
{
public Slime()
{
}
public override bool init()
{
return true;
}
public override void release()
{
}
}
}
I found the "error" there is a difference beetween mono_assembly_load and mono_domain_assembly_open. With mono_assembly_load it works.

C# DLL on Python

I'm trying to work with a C# DLL on Python. The code used to create to C# DLL is included with this question.
from ctypes import*
mydll = cdll.LoadLibrary("Z:\\9. Personal\\Jeet\\New
folder\\ClassLibrary1.dll")
mydll.Add(3,4)`
The code doesn't work and the error is:-
AttributeError: function 'Add' not found
Is there any alternative?
Iron Python is installed on the PC I'm using but I have never used it. Please give me a detailed answer with all steps.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClassLibrary1
{
// Interface declaration.
public interface ICalculator
{
int Add(int Number1, int Number2);
};
public class Class1 : ICalculator
{
public int Add(int Number1, int Number2)
{
return Number1 + Number2;
}
}
}

C# class libraries and console application

I have this class library I created. And I am running into the issue of getting the library to work with a console application. This is just a basic assignment I'm working on but I do not know where I am going wrong. Here are my files:
Class Library:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Test2
{
public class Test
{
public static string GetData(String prompt)
{
Console.WriteLine(prompt);
return Console.ReadLine();
}
public static int GetInt()
{
return Convert.ToInt32(GetData("Enter an interger"));
}
public static void put(String output)
{
Console.WriteLine(output);
}
public static void put(String output, String heading)
{
put(heading + '\n' + output);
}
public static void Main(String[] args)
{
int x = GetInt();
int y = GetInt();
if (x > 0 && y > 0) put(String.Format("Sum:{0}", x + y));
}
}
}
Console Application:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Test2;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Test myTest = new Test();
Test.GetData(prompt);
}
}
}
My main issue I guess I have is calling the methods to run in the console application. No matter how much reading I do I can't seem to know what I am doing wrong.
You don't need a Main method in your class library. The Main method in your console application is the entry point to your application.
If the functionality in the Main method you've defined in your class library is what you're trying to achieve for the overall application then consider moving that functionality into the Main method in your console application.
You've made the methods in your class library static so you don't need to instantiate an object of type Test
So my solution for you would be to have this in your Main method of your console application:
int x = Test.GetInt();
int y = Test.GetInt();
if (x > 0 && y > 0) Test.put(String.Format("Sum:{0}", x + y));
Console.Read();
I would suggest reading up on Static Classes and Static Class members here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members
When deciding to make a Static class I always come back to this quote from the above link:
A static class can be used as a convenient container for sets of
methods that just operate on input parameters and do not have to get
or set any internal instance fields.
I would also consider changing the name of your Test class. Even in small test applications like this I find it is useful to use well defined names with meaning. It's good practice so that you will do it automatically in larger applications.
Finally, good luck with all your learning!

Expected class, delegate, enum, interface, or struct (CS1518)

simple code here and the answers I find don't seem to work.
I'm using
SharpDevelop Version : 3.2.1.6466
.NET Version : 2.0.50727.5485
The problem is the error code
Expected class, delegate, enum, interface, or struct (CS1518).
Any ideas?
Program.cs codes:
using System;
using System.Threading;
namespace Threshold
{
public class Class1
{
public Class1()
{
Heritage YOLO = new Heritage();
YOLO.Fractal();
}
}
static void Main()
{
//do nothing
}
}
The cs file it calls is:
using System;
using System.Threading;
namespace Threshold
{
public class Heritage
{
int Fractal()
{
//Do stuff.
}
}
internal partial class DefineConstants
{
public const string DRIVERPATH = "d:\\tc\\bgi";
}
}
Please help with a fix.
Thanks.
Your main method is outside the class. Put it inside.

How to get my .net dll to be referenced in vba/com?

i have a .net dll which is written in c sharp, which uses linq to read data and return it to another calling vba app. i didn't plan for this so the initial project was written for it to be accessible by another .net app. so now i just found out that there's a lot of stuff about .net com interoperability and what not.
so i've learned that i need to user interface to make it work, and i need to regasm the dll to create a type library file which i can reference directly from the vba/vb6 app.
as of now i'm getting the problem of when i do this Dim obj As DtasApiTool.Program, it's fine but on the next line set obj = new DtasApiTool.Program will cause an error about New operator not being used properly. when i tested another .net dll from codeproject it works fine.
so my question is, what am i doing wrong here?
am i using too many references, as in
the using system.xxx?
or is it due
to some files that i have in the
projects, i.e. app.config
file, etc.
and how do i get the guid?
as i have very limited knowledge or experience in all of this ,i'm basing what is right or wrong from the example code in code project :( so feel free to comment on anything.
this is the code that i'm using:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Data.OleDb;
using System.Data;
using System.Xml;
using System.Xml.Linq;
using System.IO;
//using System.Security;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
//using System.Windows.Forms;
namespace DtasApiTool
{
[Guid("D6F88E95-8A27-4ae6-B6DE-0542A0FC7059")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface _Program
{
[DispId(1)]
string Get_All_Locales();
[DispId(2)]
string Get_All_Levels(string locale);
[DispId(3)]
string Get_Subjects_ByLocaleLevelId(string locale, int levelId);
[DispId(4)]
string Get_Topic_ByLevelIdLocaleSubjectId(int levelId, string locale, int subjectId);
[DispId(5)]
string Get_Subtopic_ByLevelIdLocaleSubjectIdTopicId(int levelId, string locale, int subjectId, int topicId);
[DispId(6)]
string Get_Skill_ByLevelIdLocaleSubjectIdTopicIdSubtopicId(int levelId, string locale, int subjectId, int topicId, int subtopicId);
[DispId(7)]
string Get_All_Subjects(string locale);
}
[Guid("09FE32AD-4BF8-495f-AB4D-6C61BD463EA4")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("DtasApiTool.Program")]
public class Program : _Program
{
...}
}
Hm - I'm using C# DLLs all the time with COM and never had problems. I'm not even using the interface-approach you have there. This is, for example, an anonymized part of a DLL I'm using in Microsoft Dynamics NAV via COM:
using ...;
namespace SomeCOMTool
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ProgId("MyCOMTool")]
[ComVisible(true)]
[Guid("your-guid-here without curly brackets!")]
public class MyCOMTool
{
/// <summary>
/// Empty constructor used by Navision to create a new instance of the control.
/// </summary>
public MyCOMTool()
{
}
[DispId(101)]
public bool DoSomething(string input, ref string output)
{
}
}
}
Of course "COM visible" is set in the assembly options and there's also a GUID in the respective field.

Categories