3

我有一个用 c# 编写的 .net dll,它从数据源中读取数据并充当包装器,以允许其他应用程序调用其函数来检索这些数据。问题是我没有预料到 .net dll 将用于 .net 应用程序以外的其他应用程序,所以现在我被告知这一切都将用于 vba/powerpoint 宏,我认为它与 vb6 应用程序非常相似,所以这就是我现在计划测试它的方式。

经过一番谷歌搜索,甚至在这里发布了一些问题,我设法让 dll 在 vb6 内部被引用,但是当我尝试调用一个有任何参数的函数时,我会得到错误的运行时错误消息450 错误数量的参数或无效的属性分配。

问题

那我做错了什么?有人可以提供一些资源或示例代码,我可以学习如何正确编写具有可从 vb6/vba 应用程序调用的参数的函数的 .net dll 吗?

如果我的代码有点太乱而无法阅读:),那么也许你们可以帮助告诉我如何在我从 codeproject 学习的这个示例中使用参数,当我在那里包含一些参数时它会返回相同的错误消息.

更新:

我在这里找到了另一组示例代码,但不幸的是它只将参数作为整数传递,当我尝试执行一个将参数作为字符串传递的示例函数时,我得到了同样的错误。我在这里缺少一些基础知识吗?有人在乎烧一个菜鸟吗?

更新 2:

以防万一有其他人偶然发现这个问题,我并没有真正找出导致问题的原因或原因,但由于项目仍然很小,我只是使用能够返回字符串的 dll 的工作示例正确地开始将每个函数的函数移到它上面,现在它工作正常:)

谢谢!!!

.net dll 代码如下:

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)
        {
        ...
        }
}

这就是我从 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
4

4 回答 4

2

我注意到你不见了[ComVisible(true)]

接口标头应如下所示:

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

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

类头应如下所示:

[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;
    }
}
于 2009-08-19T00:41:37.267 回答
1

尝试更改这段代码:-

[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]

[InterfaceType(ComInterfaceType.InterfaceIsDual)]

我不确定为什么您会收到错误,但是早期(或 VTable)绑定是您的 VB6 代码似乎正在尝试但InterfaceIsIDispatch不支持的错误。它可能的 VB6 本身正在回退到后期绑定,但你为什么要它呢?

如果您确实需要模拟现有的 COM 接口,还需要删除所有 DispId 属性。

于 2009-08-17T12:42:30.787 回答
1

我相信 DispID(1) 是为 ToString 或类似的东西保留的(已经有一段时间了)尝试从 2 开始您的 DispID。

于 2009-08-17T12:44:02.190 回答
1

在导出/注册程序集后,您是否尝试过查看类型库(使用 OleView.exe)?

我的怀疑是您的所有方法都返回字符串,而 COM 方法往往返回 HRESULT(不知道这是否通常是正确的 - 实际上您的示例代码项目页面似乎另有建议),这意味着您需要将输入和输出放入方法参数并使用 [in]、[out] 和/或 [retval] 显式编组它们。

无论如何,看看类型库并检查它是否看起来像您期望的那样。如果不是,您可能必须使用 MarshalAs 属性显式编组您的类型。

于 2009-08-17T13:25:30.827 回答