c写的dll的头文件如下:
extern "C" {
AMI_UTIL_API long AMI_UTIL_API_CALL createFilter(const char* option);AMI_UTIL_API long AMI_UTIL_API_CALL disposeFilter(long handle);AMI_UTIL_API long AMI_UTIL_API_CALL getProperty(long  handle, const char* propertyName, char* propertyValue, int propertyValueLength);AMI_UTIL_API long AMI_UTIL_API_CALL getNumOfProperty();AMI_UTIL_API long AMI_UTIL_API_CALL getPropertyName(int propertyId, char* propertyName, int propertyNameLength);
}C#的代码如下:
[DllImport("AmiUtil.dll", EntryPoint = "createFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr createFilter(IntPtr option);[DllImport("AmiUtil.dll", EntryPoint = "getProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern long getProperty(IntPtr handle, IntPtr propertyName, IntPtr propertyValue, int propertyValueLength);[DllImport("AmiUtil.dll", EntryPoint = "getNumOfProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern long getNumOfProperty();[DllImport("AmiUtil.dll", EntryPoint = "getPropertyName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern long getPropertyName(int propertyId, IntPtr propertyName, int propertyNameLength);[DllImport("AmiUtil.dll", EntryPoint = "disposeFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
private static extern long disposeFilter(IntPtr handle);private static int ARRAYLENTH = 1024;private void button1_Click(object sender, EventArgs e)
{
    IntPtr ptrOption = Marshal.AllocHGlobal(ARRAYLENTH);
    IntPtr ptrHandle = createFilter(ptrOption);    long lNumofProperty = getNumOfProperty();    int nLength = 0;    for (int i = 0; i < lNumofProperty; i++)
    {
        IntPtr ptrPropertyName = Marshal.AllocHGlobal(ARRAYLENTH);
        IntPtr ptrPropertyValue = Marshal.AllocHGlobal(ARRAYLENTH);        MessageBox.Show("name AllocHGlobal:" + Marshal.PtrToStringUni(ptrPropertyName));
        MessageBox.Show("value AllocHGlobal:" + Marshal.PtrToStringUni(ptrPropertyValue));        if (0 == getPropertyName(i, ptrPropertyName, nLength))
        {
            MessageBox.Show("getPropertyName error");
            return;
        }
        else
        {
            MessageBox.Show(i.ToString() + "name:" + Marshal.PtrToStringUni(ptrPropertyName));            if (0 == getProperty(ptrHandle, ptrPropertyName, ptrPropertyValue, nLength))
            {
                MessageBox.Show("getProperty error");
                return;
            }
            else
            {
                MessageBox.Show(i.ToString() + "value:" + Marshal.PtrToStringUni(ptrPropertyValue));
            }
        }        Marshal.FreeHGlobal(ptrPropertyName);
        Marshal.FreeHGlobal(ptrPropertyValue);
    }    disposeFilter(ptrHandle);    Marshal.FreeHGlobal(ptrOption);
}请问哪儿不对?
正规办法应该这么写吗?

解决方案 »

  1.   

    1、在你的上一贴已经讲过了,C++中的long在C#中是int,不是long。
    2、AMI_UTIL_API_CALL不一定是Stdcall,你得先找到它的定义。
    3、你得看原文件的说明或帮助,比如createFilter(const char* option);我的理解是你须指定一个过滤得名字。
         你的代码只传入了一个指针而没有任何的字符初始化,可能有问题。
    4、你的c中结构非常简单,完全不需要用到Marshal
    5、关键是你要理解c头文件要你做什么,要传入什么字符串参数,谁来保留内存,谁来释放内存。
      

  2.   

    4、你的c中结构非常简单,完全不需要用到Marshal
    这个提法有点问题。我原意指的是用string和StringBuilder就可以了,不用Marshal.Alloc(),不是调用过程中没有封送。
      

  3.   

    TO:gomoku 错误是
    System.AccessViolationException: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏
      

  4.   

    按照你的提示,我把c#的代码改成如下:public partial class AmiFilterTool : Form
        {
            private static int ARRAYLENTH = 1024;        [DllImport("AmiUtil.dll", EntryPoint = "createFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            private static extern int createFilter(string option);        [DllImport("AmiUtil.dll", EntryPoint = "getProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            private static extern int getProperty(int handle, string propertyName, ref string propertyValue, ref int propertyValueLength);        [DllImport("AmiUtil.dll", EntryPoint = "getNumOfProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            private static extern int getNumOfProperty();        [DllImport("AmiUtil.dll", EntryPoint = "getPropertyName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            private static extern int getPropertyName(int propertyId, ref string propertyName, ref int propertyNameLength);        [DllImport("AmiUtil.dll", EntryPoint = "disposeFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
            private static extern int disposeFilter(int handle);        public AmiFilterTool()
            {
                InitializeComponent();
            }        private void AmiFilterTool_Load(object sender, EventArgs e)
            {
                int ptrHandle = createFilter(null);            int lNumofProperty = getNumOfProperty();            int nLength = 0;            for (int i = 0; i < lNumofProperty; i++)
                {
                    string ptrPropertyName = "abcd";                if (0 == getPropertyName(i, ref ptrPropertyName, ref nLength))
                    {
                        MessageBox.Show("getPropertyName error");
                        return;
                    }
                    else
                    {
                        MessageBox.Show(i.ToString() + "name:" + ptrPropertyName);                    string ptrPropertyValue = "abcd";                    if (0 == getProperty(ptrHandle, ptrPropertyName, ref ptrPropertyValue, ref nLength))
                        {
                            MessageBox.Show("getProperty error");
                            return;
                        }
                        else
                        {
                            MessageBox.Show(i.ToString() + "value:" + ptrPropertyValue);
                        }
                    }
                }            disposeFilter(ptrHandle);
            }
        }
    现在的错误是
    if (0 == getPropertyName(i, ref ptrPropertyName, ref nLength))的地方出来PInvokeStackImbalance错误。注:AMI_UTIL_API_CALL是Stdcall
    createFilter(const char* option)可以传null
      

  5.   

    再次按照标准的类型改写后,代码如下,但是错误仍然是if (0 == getPropertyName(i, ref ptrPropertyName, ref nLength))的地方出来PInvokeStackImbalance错误。 private static int ARRAYLENTH = 1024;[DllImport("AmiUtil.dll", EntryPoint = "createFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern System.Int32 createFilter(System.String option);[DllImport("AmiUtil.dll", EntryPoint = "getProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern System.Int32 getProperty(System.Int32 handle, System.String propertyName, ref System.Text.StringBuilder propertyValue, ref System.Int16 propertyValueLength);[DllImport("AmiUtil.dll", EntryPoint = "getNumOfProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern System.Int32 getNumOfProperty();[DllImport("AmiUtil.dll", EntryPoint = "getPropertyName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern System.Int32 getPropertyName(System.Int16 propertyId, ref System.Text.StringBuilder propertyName, ref System.Int16 propertyNameLength);[DllImport("AmiUtil.dll", EntryPoint = "disposeFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern System.Int32 disposeFilter(System.Int32 handle);public AmiFilterTool()
    {
        InitializeComponent();
    }private void AmiFilterTool_Load(object sender, EventArgs e)
    {
        System.Int32 handle = createFilter(null);    System.Int32 numofProperty = getNumOfProperty();    System.Int16 length = 0;    for (System.Int16 i = 0; i < numofProperty; i++)
        {
            System.Text.StringBuilder propertyName = new StringBuilder(ARRAYLENTH);        if (0 == getPropertyName(i, ref propertyName, ref length))
            {
                MessageBox.Show("getPropertyName error");
                return;
            }
            else
            {
                MessageBox.Show(i.ToString() + "name:" + propertyName);            System.Text.StringBuilder propertyValue = new StringBuilder(ARRAYLENTH);            if (0 == getProperty(handle, propertyName.ToString(), ref propertyValue, ref length))
                {
                    MessageBox.Show("getProperty error");
                    return;
                }
                else
                {
                    MessageBox.Show(i.ToString() + "value:" + propertyValue);
                }
            }
        }    disposeFilter(handle);
    }
    }
      

  6.   

    不好意思,上面的代码的错误是
    System.AccessViolationException: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏 
      

  7.   


    [DllImport("AmiUtil.dll")] 
    static extern int getPropertyName(string propertyName1, StringBuilder propertyName2, int propertyNameLength);  
                   |                    |                          |
    AMI_UTIL_API long getPropertyName(const char* propertyName1, char* propertyName2, int propertyNameLength); 
    long        -> int
    const char* -> string
    char*       -> StringBuilder并这样调用: C# code
    int length = 128;
    string propertyName1 = "you need to specify a string here";
    StringBuilder propertyName2 = new StringBuilder( length );
    getPropertyName(propertyName1, propertyName2, length); 
      

  8.   

    上个帖子的代码和我这次好像就是在输出参数的地方,我多写了ref
    这个ref需要不需要?
      

  9.   

    我已经完全按照你的意思修改了一边代码,如下:
    private static int ARRAYLENTH = 1024;[DllImport("AmiUtil.dll", EntryPoint = "createFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern int createFilter(string option);[DllImport("AmiUtil.dll", EntryPoint = "getProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern int getProperty(int handle, string propertyName, StringBuilder propertyValue, int propertyValueLength);[DllImport("AmiUtil.dll", EntryPoint = "getNumOfProperty", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern int getNumOfProperty();[DllImport("AmiUtil.dll", EntryPoint = "getPropertyName", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern int getPropertyName(int propertyId, StringBuilder propertyName, int propertyNameLength);[DllImport("AmiUtil.dll", EntryPoint = "disposeFilter", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
    private static extern int disposeFilter(int handle);public AmiFilterTool()
    {
        InitializeComponent();
    }private void AmiFilterTool_Load(object sender, EventArgs e)
    {
        int handle = createFilter(null);    int numofProperty = getNumOfProperty();    int length = 0;    for (int i = 0; i < numofProperty; i++)
        {
            StringBuilder propertyName = new StringBuilder(ARRAYLENTH);        if (0 == getPropertyName(i, propertyName, length))
            {
                MessageBox.Show("getPropertyName error");
                return;
            }
            else
            {
                MessageBox.Show(i.ToString() + "name:" + propertyName.ToString());            StringBuilder propertyValue = new StringBuilder(ARRAYLENTH);            if (0 == getProperty(handle, propertyName.ToString(), propertyValue, length))
                {
                    MessageBox.Show("getProperty error");
                    return;
                }
                else
                {
                    MessageBox.Show(i.ToString() + "value:" + propertyValue.ToString());
                }
            }
        }    disposeFilter(handle);
    }
    我们先不管运行结果对不对,我问一下,托管代码调用非托管代码的时候,不需要使用Marshal来分配非托管的内存吗?
    我这块不明白,所以对这段代码没有信心。总感觉需要使用Marshal似的。麻烦你给我解释一下。
      

  10.   

    凡是需要char*之类的参数的函数
    CharSet = CharSet.Unicode
    一律改为
    CharSet = CharSet.Ansi
    [DllImport("AmiUtil.dll", EntryPoint = "createFilter", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 
    private static extern int createFilter(string option); [DllImport("AmiUtil.dll", EntryPoint = "getProperty", CharSet = CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 
    private static extern int getProperty(int handle, string propertyName, StringBuilder propertyValue, int propertyValueLength); [DllImport("AmiUtil.dll", EntryPoint = "getNumOfProperty", CharSet = CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 
    private static extern int getNumOfProperty(); [DllImport("AmiUtil.dll", EntryPoint = "getPropertyName", CharSet = CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] 
    private static extern int getPropertyName(int propertyId, StringBuilder propertyName, int propertyNameLength); [DllImport("AmiUtil.dll", EntryPoint = "disposeFilter", CallingConvention = CallingConvention.StdCall)] 
    private static extern int disposeFilter(int handle); 
      

  11.   

    谢谢,现在运行结果先不管他。关于托管代码调用非托管代码的时候,需要不需要使用Marshal来分配非托管的内存,
    需要不需要使用Marshal。麻烦给我解释一下。谢谢.
      

  12.   

    看具体情况const char* 参数的话 假如你定义成int类型的,哪么你就需要自己分配内存,自己拷贝数据过去
    你要指定字符集用string的话就不需要了.
    char*也类似.建议lz先看看C程序设计.然后再看函数的规定.
      

  13.   

    越来越糊涂了。gomoku 人了?
      

  14.   

    :D
    看来你还得看看内存管理相关的东西只要你能保证传递的内容合法.分配不分配都行.
    你的问题在于字符集, CharSet.Unicode的时候,会认为指针是unicode编码的,但你传递的是char*.这点不符合.
      

  15.   

    我给你提供两个文件,都可以用命令行编译。你可以用它们做试验。
    先进入命令行(以便获得正确途径),程序 -> VS2005 -> VS2005 Tools -> VS2005 Comman Prompt
    编译C++ dll:  cl /LD MyDll.cpp
    编译C#程序:    csc TestMyDll.cs
    运行C#程序:    TestMyDll.exe
    // MyDll.cpp
    // compile with:  cl.exe /LD MyDll.cpp#include <strsafe.h>extern "C"{
    __declspec(dllexport) long _stdcall getPropertyName(const char* name, char* value, int length)
    {
    StringCchCopyA(value, length, "hello ");  //拷贝"hello "到value
    StringCchCatA(value, length, name);       //连接字符串 "hello " + name return 123;
    }
    }// TestMyDll.cs
    // compile with:  csc TestMyDll.cs
    using System;
    using System.Text;
    using System.Runtime.InteropServices;class Program
    {
        static void Main(string[] args)
        {
            int maxLength = 64;
            StringBuilder property = new StringBuilder(64);                                // 准备接受字符串        int result = getPropertyName("John", property, maxLength);                     // = 123
            Console.WriteLine("result:{0}  property: {1}", result, property.ToString());   // hello John
            Console.ReadKey();
        }
        [DllImport("MyDll.dll", CharSet = CharSet.Ansi)]
        extern static int getPropertyName(string name, StringBuilder property, int length);
    }
      

  16.   

    To:akirya 
    c写的dll是CharSet.Unicode,我上面代码传递的是string和stringbuffer,我应该换成什么才不是char*
    To:gomoku 
    谢谢,我试试看
      

  17.   

    to:gomoku  
    extern static int getPropertyName(string name, StringBuilder property, int length);
    的最后一个参数int length也是输出参数,这么些好像没有取得值。
      

  18.   


    按照你上贴的写法,不是输出参数:
    extern "C" { 
    AMI_UTIL_API long AMI_UTIL_API_CALL getPropertyName(const char* propertyName1, char* propertyName2, int propertyNameLength); 

      

  19.   


    呃,你给的代码明明是char* ,是ansi编码的要是unicode编码的话应该是wchar_t*类型的
      

  20.   

    To:akirya   
    明白了,非常感谢。
    To:gomoku   
    还有一部分我没贴出来,其中有的函数是输出参数。
    如果是输出参数的话,应该怎么弄? 
      

  21.   


    bool getDonation(int* amount)
    {
       *amount = 200;        //not much
       return true;          //but I did
    }[DllImport("...")]
    extern static bool getDonation(ref int amount);