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.
Related
I am having a DLL file. With the use of DLL, I have to call the methods and add some more methods in my project. Now, I need to migrate the older DLL to Make that project as a new DLL. I done this But the problem is The C# code is converted to net module it shows two errors. I am not clear about that. kindly help me over it.
DLL Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace mcMath
{
public class mcMathComp
{
private bool bTest = false;
public mcMathComp()
{
// TODO: Add constructor logic here
}
/// <summary>
/// //This is a test method
/// </summary>
public void mcTestMethod()
{ }
public long Add(long val1, long val2)
{
return val1 - val2;
}
/// <summary>
/// //This is a test property
/// </summary>
public bool Extra
{
get
{
return bTest;
}
set
{
bTest = Extra;
}
}
}
}
CS Project:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using mcMath;
namespace mcClient
{
class Program
{
static void Main(string[] args)
{
mcMathComp cls = new mcMathComp();
long lRes = cls.Add(23, 40);
cls.Extra = false;
Console.WriteLine(lRes.ToString());
Console.ReadKey();
}
}
}
Errors:
Program.cs(5,7): error CS0246: The type or namespace name 'mcMath' could >not be found (are you missing a using directive or an assembly reference?)
Tried Methods:
I will add the reference via Project-> Add Reference.
The using Reference also used.
Put the DLL into the current project debug/release folder
I'm guessing you used to have the code side by side, i.e.
public int Add(int a, int b)
{
return a + b;
}
public void SomeMethod()
{
var result = Add(2,3);
}
This works because the scope (this.) is applied implicitly, and takes you to the Add method on the current instance. However, if you move the method out, the scope is no longer implicit.
You will need one of:
the type name if it is a static method
or a static using if using C# 6
a reference to the instance if it is an instance method
Then you would use one of (respectively):
var result = YourType.Add(2,3); (plus using YourNamespace; at the top)
using static YourNamespace.YourType; at the top
var result = someObj.Add(2,3);
Checking the compiler message, it sounds like you've done something like (line 7):
using YourNamespace.YourType.Add;
which is simply wrong; you don't use using to bring methods into scope - only namespaces and (in C# 6) types.
Likewise, I suspect you have (line 22):
var result = YourNamespace.YourType.Add(x,y);
which is not valid as this is not a static method.
Create and Using DLL in same Project in c#
DLL or Class Library is a separate project that can be part of same solution.
As you already know, adding a reference to that dll/project will make it available in your app project.
However if function Add in dll is in different namespace (which would be normal) u would need to add using clause at the beginning of your class
The Question In a Nutshell:
How can I get the size of a user defined structure passed as an object to a class library?
Overview:
The point of this test project is to build a class library that wraps up memory sharing using memory mapping within .Net 4 (Or less). In the end I want to be able to define a structure in my main application, pass it to the class library wrapper and let the class library determine the size of the structure.
Definitions:
MyAppA: Main application, the one that will create the memory map
instance initially.
MyAppB: Second application used to communicate
with MyAppA. This will tap into the existing memory map.
MemoryMapTool: This will be the class library that wraps up all the memory sharing.
TestStruct: This will be the structure defined in MyAppA and MyAppB that will be the exact same in both applications but might change from time to time. MemoryMapTool would NOT know the structure layout at any time, it will simply see it as an object.
Initial Idea:
I want the class library wrapper to have no knowledge of the TestStruct generated by MyAppA other than it is an object that the class library wrapper needs to keep up with and use for memory sharing...
I was thinking I would create the TestStruct in MyAppA and add as many variables to it as I needed (in this case just 1, a string). Then pass it into the MemoryMapTool constructor and let the MemoryMapTool class determine the size of the struct. This is currently the issue. Working with memory I tend to be cautious and research before I just try something that will possibly fail killing my IDE or OS... ;)
I was originally going to just pass the TestStruct straight to the MemoryMapTool constructor but ran into this issue...
long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(oData));
Error: The type or namespace name 'oData' could not be found (are you missing a using directive or an assembly reference?)
I then was thinking of trying to just use...
long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(oData);
... and it seems to work (at least the IDE likes it). But for some reason I don't feel like that's the correct way to do it.
UPDATE: After trying that I get a new error...
Error: Type 'MyAppA.Form1+TestStruct' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.
Current Source
MemoryMapTool.cs Contents
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.MemoryMappedFiles;
using System.Threading;
namespace SharedMemoryWorker
{
public class MemoryMapTool : IDisposable
{
#region Private class variables
private string m_sLastError = "";
private MemoryMappedFile mmf = null;
private string m_sMapName = "";
private object m_oData = null;
#endregion
#region Public properties
public string MapName
{
get
{
return m_sMapName;
}
set
{
m_sMapName = value;
}
}
public object Data
{
get
{
return m_oData;
}
set
{
m_oData = value;
}
}
#endregion
#region Constructor
private MemoryMapTool(string sMapName, object oData)
{
long lMapSize = System.Runtime.InteropServices.Marshal.SizeOf(oData);
try
{
//Save the map name
m_sMapName = sMapName;
//Create new map or use an existing one
//mmf = MemoryMappedFile.CreateOrOpen(m_sMapName, lMapSize);
}
catch (Exception ex)
{
m_sLastError = ex.Message;
throw new NullReferenceException("Error creating new object!");
}
}
public void Dispose()
{
//Deconstructor
}
#endregion
#region Public class methods
public string GetLastError()
{
return m_sLastError;
}
#endregion
}
}
MyAppA, Form1.cs Contents
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MyAppA
{
public partial class Form1 : Form
{
#region Public structures
public class TestStruct
{
#region Private class variables
private string m_sTest = null;
#endregion
#region Public properties
public string Test
{
get
{
return m_sTest;
}
set
{
m_sTest = value;
}
}
#endregion
}
#endregion
public Form1()
{
InitializeComponent();
}
}
}
MyAppB, Form1.cs Contents
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace MyAppB
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
I think the error is pretty clear. You need to tell the CLR how you want the structure or class members to be laid out in memory. See the StructLayoutAttribute - it needs to be applied explicitly to classes (whereas for structs Sequential is the default).
From your description it appears that you want to do IPC between two or more managed processes. You may also want to establish a unified marshaling strategy for strings (see the MarshalAsAttribute). You can choose one and stick with it throughout your classes.
Finally, I would like to say that this isn't really appropriate for what you're trying to do (too much overhead and room for error). Instead you could:
Still use MMFs but serialize your classes using binary serialization or even JSON.
Design a service oriented architecture based on either WCF or WebAPI (can be selfhosted now, via OWIN/Katana).
Ultimately you can use raw TCP/IP sockets as well and design a small protocol for your apps.
My choice would be #2. Performance can be very good, especially with WCF and on the same computer with named pipes or net.tcp bindings and it just works.
I am working with a C# project that I did not create. I stripped it down very much.
I thought I did everything correctly. It compiles fine, and I can reference the DLL in e. g. a VB.NET application and use it.
However, the DLL's methods are not exposed.
Can somebody tell me if he seems something wrong immediately?
This is the important part of the DLL, I think:
using System;
using System.Collections;
using System.Data;
using System.Reflection;
using System.Text;
using SevenZip.Compression.LZMA;
namespace SevenZipControl
{
public static class Zipper
{
public static bool compressBytes(byte[] InputBytes,out byte[] OutputBytes)
{
OutputBytes=SevenZipHelper.Compress(InputBytes);
return true;
}
public static bool decompressBytes(byte[] InputBytes, out byte[] OutputBytes)
{
OutputBytes = SevenZipHelper.Decompress(InputBytes);
return true;
}
}
}
This is how I would use it in VB.NET:
Dim c As SevenZipControl.Zipper
c. (...)
But my functions "compressBytes" and "decompressBytes" are not available as one can see in this screenshot:
Your code lists static methods. You are creating an instance of the SevenZipControl.Zipper class.
To piggyback on #JAnderson's answer, you shouldn't need to instantiate with Dim. The following should work:
SevenZipControl.Zipper.compressBytes(data)
For some info on how the notion of static classes translates into VB, see this discussion
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.
I am converting a VB.Net project to C#. I declare some public structures in one module and access them in another. I get a "The type or namespace name 'ItemInfo' could not be found." - where ItemInfo is one of the structures. Here is code snippet where I declare the structure:
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Data.Common;
using System.IO;
using System.Net.Mail;
using dbAutoTrack.DataReports;
using dbAutoTrack.DataReports.PDFExport;
namespace PlanSchedule
{
static class PlanScheduleBLL
{
#region "Structures"
public struct ItemInfo
{
// LinearProcessTime = sum of bll level resource requirements;
// when more than one resource on a level, uses max time required;
// not saved in database, used in order processing only
public string ItemNumber;
public string Description;
public string MakeBuy;
public long TotalShelfLife;
public long ReceivedShelfLife;
public float YieldPercentage;
public float BatchSize;
public float SafetyStock;
}
...and this is where I reference it:
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
namespace PlanSchedule
{
public partial class BuildToStockOrderEntry
{
// slItemList
// Key = <item number> - <item description> Value = item number
private SortedList<string, string> slItemList = new SortedList<string, string>();
// slItems
// Key = item number Value = item info
private SortedList<string, ItemInfo> slItems = new SortedList<string, ItemInfo>();
I do not know C# so I may have a problem in my syntax. Is my declaration of the SortedList using ItemInfo correct?
TIA,
John
You currently have the ItemInfo struct nested in the static class PlanScheduleBLL. You have two options:
Pull it out of that class, and just into the namespace.
If you really want it nested, you need to make PlanScheduleBLL public, and refer to it like PlanScheduleBLL.ItemInfo
It's because you are declaring the struct inside another class. You could reference it by doing:
private SortedList<string, PlanScheduleBLL.ItemInfo> slItems ...
But a much better approach would be to just define the struct outside of the class and directly in the namespace itself (i.e. get rid of the PlanScheduleBLL static class).
Place the ItemInfo structure outside of the PlanScheduleBLL class.