views:

492

answers:

4

hi guys,

i have a .net dll written in c# which reads data off a data source and acts as a wrapper to allow other apps to call its function to retrieve those data. the thing is i didn't anticipate the .net dll to be used for other than .net apps, so now i was told this all is going to be used in a vba/powerpoint macro which i figure is rather similar to a vb6 app, so that is how i'm planning to test it right now.

after some googling and even posting some question here in so, i manage to get the dll to be referenced inside of vb6, but when i try to call a function which have any parameters, i'll get the error run-time message of error 450 wrong number of arguments or invalid property assignment.

Questions

So what am i doing wrong? and can someone please provide some resources or sample codes that i can learn on how to properly write a .net dll which has functions with parameters that can be called from vb6/vba app?

And if my codes are a bit too messy to read :), then perhaps you guys can help to tell me how to put parameters to work in this sample which i learn from codeproject, its returning the same error message when i include some parameters there.

UPDATES:

i found another set of sample codes here, but unfortunately it only passes parameters as integer, and when i try to do a sample function which passes parameters as string, i get the same error. Am i missing something fundamentals here? anybody care to flame a noobie?

Updates 2:

Just in case there's anybody else who stumbled onto this problem, i didn't really found out why or what was causing the problem but since the project was still rather small, i just use a working sample for the dll which was able to return string properly and start moving function per function over to it, and it's working fine now :)

thanks!!!

the .net dll codes are as follows:

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-0542A0FC7040")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface _Program
    {
        [DispId(1)]
        string Get_All_Locales(string test);

        [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);

        [DispId(8)]
        void Constructor(string directory);

    }

    [Guid("13FE32AD-4BF8-495f-AB4D-6C61BD463EA5")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("DtasApiTool.Program")]
    public class Program : _Program
    {
        private string connStr = "";
        private string xmlLocation = "";

        public Program(){

        }

        public void Constructor(string directory)
        {
          ...  
        }


        #region This part contains all the internal functions

        /// <summary>
        /// return the component lesson given a locale and skill id
        /// </summary>
        /// <param name="locale"></param>
        /// <param name="skillId"></param>
        /// <returns></returns>
        internal string Get_Component_Lesson(string locale, string skillId)
        {
            ...
        }

        /// <summary>
        /// return a xmlFile containing all the information from the Skill Analysis
        /// </summary>
        /// <param name="fileLocation">raw xml file location, i.e. C://datapath/raw_dato.xml</param>
        /// <returns> the location of the output xml file.</returns>
        internal string Process_Skill_Analysis_Report(string fileLocation)
        {            
...
}

        #endregion

        /// <summary>
        /// Returns all the locale which is in the database currently.
        /// </summary>
        /// <returns></returns>
        public string Get_All_Locales(string test)
        {
        ...
        }
}

and this is how i'm calling it from vb6:

Option Explicit

Private Sub Form_Load()        
    Dim obj As DtasApiTool.Program
    Set obj = New DtasApiTool.Program

    Dim directory As String
    directory = """" + "C:\Documents and Settings\melaos\My Documents\Visual Studio 2008\Projects\app\bin\Release\" + """"

    'obj.Constructor directory
    Dim func As String
   func = obj.Get_All_Locales(directory)

End Sub
+1  A: 

Try changing this chunk of code:-

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]

to

[InterfaceType(ComInterfaceType.InterfaceIsDual)]

I'm not sure why you get the error you are however early (or VTable) binding is what you VB6 code appears to be attempting but InterfaceIsIDispatch doesn't support that. Its possible VB6 itself is falling back to late binding but why would you want it to?

Also drop all the DispId attributes they are only needed if you really need to emulate an existing COM interface.

AnthonyWJones
@Anthony, thanks, i don't get the error message anymore, but i'm still getting no data from the functions :(
melaos
@Melaos: At least that is progress ;). I would now suggest that you compile your VB6 app, run it and attach the VS debugger to it. Make sure the code in your .NET dll is doing what you think its doing and is receiving the parameters you think it is.
AnthonyWJones
@Anythony, yea, i'm glad that at least i can bypass the error part, and i'm actually trying to learn how to debug this whole thing now, seeing i'm using vs.net express, i can't do the attach debugging method :(
melaos
Have you tested the .NET on its own in VS Express? Can you build a VB6 mockup for your .NET component and test your VB6 code with that?
AnthonyWJones
@Anthony, i built another c# app to test the dll as i didn't realize that the dll was going to be referenced by a com app :(, and are you suggesting for me to build the dll in vb6?
melaos
No I'm just suggesting you test the .NET side and the VB6 side separately before trying to merge them. I have to doubt that Interop itself is for some reason failing to return a value, its more likely that the .NET component just isn't doing what you expect it to when called from VB6. Frankly this sort of work is very difficult if you don't have the right tools.
AnthonyWJones
+1  A: 

I believe DispID(1) is reserved for ToString or something like that (it's been a while) Try starting your DispIDs at 2.

Eric Nicholson
This isn't true, the only reserved DispIDs are 0 and below, for example -4 is used to acquire the IEnumVariant that supports For Each.
AnthonyWJones
My bad, it looks like ToString is in fact 0 on _Object. Thanks!
Eric Nicholson
+1  A: 

Have you tried looking at the type library (using OleView.exe) after you exported/registered the assembly?

My suspicion is that all your methods return strings, whereas COM methods tend to return HRESULT's (no idea if this is generally true - actually your example codeproject page seems to suggest otherwise), which means you would need to put your input and output into the method arguments and explicitly marshal them with [in], [out], and/or [retval].

Anyways, take a look at the type library and check if it looks like you expect it to look. If not you may have to explicitly marshal your types using the MarshalAs attribute.

Andreas F
+2  A: 

I noticed you're missing [ComVisible(true)].

Interface header should look like:

[Guid("CF4CDE18-8EBD-4e6a-94B4-6D5BC0D7F5DE")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[ComVisible(true)]
public interface IFoo {

    [DispId(1)]
    string MyMethod(string value);
}

Class header should look like:

[Guid("7EBD9126-334C-4893-B832-706E7F92B525")]
[ClassInterface(ClassInterfaceType.None)]
[ComVisible(true)]
[ProgId("MyNamespace.Foo")]
public class Foo: IFoo {

    public string MyMethod(string value){
        return somestring;
    }
}
C-Pound Guru