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);
}请问哪儿不对?
正规办法应该这么写吗?
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);
}请问哪儿不对?
正规办法应该这么写吗?
2、AMI_UTIL_API_CALL不一定是Stdcall,你得先找到它的定义。
3、你得看原文件的说明或帮助,比如createFilter(const char* option);我的理解是你须指定一个过滤得名字。
你的代码只传入了一个指针而没有任何的字符初始化,可能有问题。
4、你的c中结构非常简单,完全不需要用到Marshal
5、关键是你要理解c头文件要你做什么,要传入什么字符串参数,谁来保留内存,谁来释放内存。
这个提法有点问题。我原意指的是用string和StringBuilder就可以了,不用Marshal.Alloc(),不是调用过程中没有封送。
System.AccessViolationException: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏
{
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
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);
}
}
System.AccessViolationException: 尝试读取或写入受保护的内存。这通常指示其他内存已损坏
[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);
这个ref需要不需要?
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似的。麻烦你给我解释一下。
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);
需要不需要使用Marshal。麻烦给我解释一下。谢谢.
你要指定字符集用string的话就不需要了.
char*也类似.建议lz先看看C程序设计.然后再看函数的规定.
看来你还得看看内存管理相关的东西只要你能保证传递的内容合法.分配不分配都行.
你的问题在于字符集, CharSet.Unicode的时候,会认为指针是unicode编码的,但你传递的是char*.这点不符合.
先进入命令行(以便获得正确途径),程序 -> 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);
}
c写的dll是CharSet.Unicode,我上面代码传递的是string和stringbuffer,我应该换成什么才不是char*
To:gomoku
谢谢,我试试看
extern static int getPropertyName(string name, StringBuilder property, int length);
的最后一个参数int length也是输出参数,这么些好像没有取得值。
按照你上贴的写法,不是输出参数:
extern "C" {
AMI_UTIL_API long AMI_UTIL_API_CALL getPropertyName(const char* propertyName1, char* propertyName2, int propertyNameLength);
}
呃,你给的代码明明是char* ,是ansi编码的要是unicode编码的话应该是wchar_t*类型的
明白了,非常感谢。
To:gomoku
还有一部分我没贴出来,其中有的函数是输出参数。
如果是输出参数的话,应该怎么弄?
bool getDonation(int* amount)
{
*amount = 200; //not much
return true; //but I did
}[DllImport("...")]
extern static bool getDonation(ref int amount);