VS2005出现编译错误: fatal error C1902: Program database manager mismatch; please check your installation
复制3个文件(mspdb80.dll, mspdbsrv.exe and mspdbcore.dll), 从C:Program FilesMicrosoft Visual Studio 8Common7IDE 到 C:Program FilesMicrosoft Visual Studio 8VCbin
即可.其中mspdb80.dll可能会已经存在,无所谓了.
C++使用 void extern __declspec(dllexport) 函数名()定义的输出函数, 在C#中调用时, 如前文所述, 使用
[DllImport("D:\VS2005Projects\Dev_PetroSim2010b\Dev_AMDBR\Debug\Dev_DR_AMDBR.dll", EntryPoint = "#1")]
public static extern void amDBRSurfaceTensionEX(int compCount, int[] components, int modelID, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] double[] Z, ref int errorID);
来调用.
这里注意的是,定义函数入口点的参数EntryPoint = "#1", C#中有2种方式可以定义入口点, 一种是以序号加前缀"#",这是我一直用的,另一种是用EntryPoint="函数名",这种方法我试了很多次却无法实现,C#编译器总会说在DLL中没有找到函数.
如何找到函数所对应的EntryPoint编号呢?我用的是一个叫DLLExportViewer的软件,可以告诉你各个函数的Ordinal,即编号,照此调用即可,但是问题是,如果原DLL进行编辑之后,增加或减少输出函数之后,各个函数的编号会重新打乱,使原程序无法进行.所以对这种情况,即DLL尚在编辑之中时,还是以函数名作为EntryPoint比较好.
在C:Program FilesMicrosoft Visual Studio 8VCbin目录中,有一个文件dumpbin.exe文件和DLLExportViewer一样可以查看DLL的输出函数,但是在运行时,居然跳出一个错误窗口
查找后发现,mspdb80.dll文件是在C:Program FilesCommon FilesMicrosoft SharedVSA8.0VsaEnv中,将此文件复制到C:Program FilesMicrosoft Visual Studio 8VCbin目录,再运行时错误消失.
查阅dumpbin的输出,发现输出函数说明及如下
| ordinal | hint | RVA | name |
| 7 | 6 | 0002D591 | ?amDBRSurfaceTensionEX@@YAXHPAHHPANAAH@Z = @ILT+1420(?amDBRSurfaceTensionEX@@YAXHPAHHPANAAH@Z) |
前面的ordinal 7即为函数的输出序号, name列即为可用的函数名,在C#中,改函数声明为如下即可用函数名作为EntryPoint
[DllImport("D:VS2005ProjectsDev_PetroSim2010bDev_AMDBRDebugDev_DR_AMDBR.dll", EntryPoint = "?amDBRSurfaceTensionEX@@YAXHPAHHPANAAH@Z")]
============================================
照上述做法,导出函数可以成功,但是在C#中调用却是相当麻烦,因为函数的入口名称太过繁杂且不可理解,因此,还是要解决在C++中的函数输出,以使在C#中能直接以函数名作为入口.
上述问题中,实际上是在C++中函数输出时,默认输出名是使用C++的mangled name, 要使用C的命名方式方可直接使用其输出名,这时,函数输出声明应改为如下:
extern "C" __declspec(dllexport) void amDBREnthalpyEX(int CompCount, int* streamComp, int modelID, double* streamInfo, int& errorID)
这样,可以看到DLLExportViewer的查看结果了,以此方式声明的amDBREnthalpyEX函数和以原方式声明的其他函数的名称的不同如下图所示.
这时,在C#中,就可以以函数名称作为入口点了:
[DllImport("D:VS2005ProjectsDev_PetroSim2010bDev_AMDBRDebugDev_DR_AMDBR.dll", EntryPoint = "amDBREnthalpyEX")]
public static extern void amDBREnthalpyEX(int compCount, int[] components, int modelID, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] double[] Z, ref int errorID);
在托管代码和本地代码之间传递数组,是interop marshaling中间比较复杂的一个问题。本文从数组的定义开始,介绍数组marshalling的三种方法,并对blittable类型等概念做进一步的讨论。
当托管代码需要和本地代码互操作时,我们就进入了interop的领域。interop的场景形形色色,不变的是我们需要把数据从一个世界marshal到另一个世界。
在讨论数组marshalling之前,请各位和我一起思考一个问题,什么是数组?之所以要讨论这个问题,原因在于不同的术语在不同的语境中含有不同的意思。在使用c语言的时候,我认为数组就是一个指针。但是熟悉c#的朋友可能不同意我的观点,数组是System.Array或者Object[]。我认为,这两种回答都是出自语言领域的正确观点。那么如果有一个项目含有两个模块,一个用本地代码撰写,另一个用托管代码撰写,两者之间的接口要求传递一个数组,这个”数组”包含着怎样的语义呢?我觉得有两点是很重要的:
1. 如何访问数组元素。就好比c语言中的数组指针,c#中的数组引用,都是访问数组必不可少的线索。
2. 数组的大小。数组的大小不仅仅是System.Array.Length。它还可以包括诸如数组的维数,每个维上的启始边界和结束边界。
.NET在marshal数组的时候,很大程度上也是从以上两点出发,架起托管世界和本地代码之间的桥梁。根据操作的具体数据类型不同,数组marshal又可以分为以下两个大类,三个小类,我们分别介绍:
1. 数组作为参数传递
a) c/c++类型的数组
c类型的数组,也就是由指针指明存储空间首地址的数组,是一个自描述很低的数据结构。尽管有些编译器支持在固定偏移量上写入数组的长度,但是因为各个编译器处理的具体方法不同,没有一个标准让CLR来参考。所以我们在marshal一个c类型的数组的时候,不得不用其他方法考虑传递数组的大小,有以下两种:
1) 约定指针数组长度
这种方法需要调用者和被调用者之间有一个约定,给出一个数组长度的固定值。在托管端声明一个interop方法的时候,只需要用SizeConst这个属性,把这个约定告诉CLR。CLR在进行Marshal的时候,会根据这个值,在本地堆上分配相应的空间。
public static extern void Ex([In, Out][MarshalAs(UnmanagedType.LPArray, SizeParamConst=3)]string[] a);
2)通过一个额外的参数指定数组长度
可能有的朋友觉得,约定一个静态的数组长度还不够灵活,希望能够动态的传递数组的长度,从而使得数组marshalling不必受制于固定数组长度的限制。我们先来看普通的函数调用是如何解决这个问题的:caller和callee通过约定一个额外的参数,来传递数组的长度,这可以被视作是一个调用者和被调用者的约定。由于marshalling需要clr的参与,那么就需要把这个约定用clr能够理解的方式,进行扩充,形成一个三方约定。CLR,用属性SizeParamIndex来描述此类约定。
public static extern void Ex2([In, Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)]string[] a,int len);
b) SafeArray
SafeArray是COM引入的数据类型,是一个自描述度很高的数据结构。他可以很清楚的告诉用户,该数组的元素类型,数组包含了多少维,每一维的起始位置和终止位置。所以marshal这类safearray的时候,只需要通过设定属性,告诉CLR,当前array对应的本地代码是safearray即可。举例如下:
public void DumpSafeArrayStringIn( [In][MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VarEnum.VT_BSTR)]Object[] array);
大家可以看到,SafeArraySubType可以用来指定数组元素的类型
2. 数组作为字段传递
很久以来,对于interop,一直有这样的评价,简单数据结构的marshalling其实并不复杂,但是一旦进入了struct或者class这种你中有我,我中有你的层叠数据结构之后,marshalling就成了bug的温床。所以在这里,我们也要提提数组作为struct/class的一个字段的方法。在这里首先要给这个stuct/class加一个限制,是byval。由于这个限制,大家可以想象的出,CLR在marshal的时候,做的事情是类似于浅copy的内存复制,所以对数组marshal的时候,也就只支持固定长度的数组marshal。
- public class StructIntArray
- {
- [MarshalAs(UnmanagedType.ByValArray, SizeConst=4)]
- public int[] array;
- }
数组作为一种常用的数据结构,各种高级语言都提供了相应的支持,在这些高级语言之间交互操作的时候,数组也是传送集合类型数据的重要结构,希望今天的内容能对大家有所帮助。
帮老婆的弟媳做一个网站,下载了一份现成的PHP代码,不过都是加了密的.有些改动需要对PHP文件进行,可是发现这份东西是用了ZEND加密的,网上现成的DEZENDER的软件不少,绝大多数是基于黑刀改装的,效果不错,可是都不能对目录结构进行原样复制.找到一份梅花三弄版 DEZENDER据说可以保持原目录结构,可是不知是什么原因,居然在我的计算机上运行不起来.于是我就想自己写一个程序来做这事,顺便学习一下C#.
Form上的几个基本控件:
- txtFolder: PHP文件所在的根目录, 新目录将自动取名为目录名_new
- button1: txtFolder的browse按钮
- CheckBox1: With Subfolders 标记, 即操作是否包括各级子目录
- CheckBox2: Process PHP files only 标记, 即操作是否只针对PHP文件,如果要针对所有文件,则非PHP文件将被复制到指定目录
- txtLog: 日志框, 输出所有信息
- stsInfo: StatusBar, 显示一些提示信息
- btnList: 显示所有文件到txtLog
- btnbProcess: 对文件进行操作
几个private变量:
- private bool WithSub = true; //对应checkbox1
- private bool PhpOnly = false; //对应checkbox2
- private string RootDir; //对应txtFolder
检查运行环境, PHP.EXE是黑刀的执行文件, 必须在当前目录下存在.
- private void Form1_Load(object sender, EventArgs e)
- {
- if (!File.Exists(".php.exe"))
- {
- MessageBox.Show("No php.exe found, the program may not run properly!");
- Application.Exit();
- }
- dlgOpenFld.SelectedPath = "D:temp";
- }
另外, 在PHP.INI文件中, 要确认以下文件的路径名正确, 个人感觉用相对路径会好一些
- [Zend]
- zend_extension_manager.optimizer_ts="libOptimizer-3.3.0"
- zend_extension_ts="libZendExtensionManager.dll"
下面, BROWSE文件夹操作.
- private void button1_Click(object sender, EventArgs e)
- {
- if ( dlgOpenFld.ShowDialog() == DialogResult.OK )
- {
- txtFolder.Text = dlgOpenFld.SelectedPath;
- }
- }
最重要的操作之一, 用递归取得文件列表, 在这一步里,用到了List<>这样的数据类型, 这是用于未知长度数组的, 可以看到, C#对于文件,目录的操作都与DELPHI有了非常大的不同, 这就是FRAMEWORK:
- private List<FileInfo> getFiles(string tDir, bool SubFolder)
- {
- List<FileInfo> FileList = new List<FileInfo>();
- DirectoryInfo fDir = new DirectoryInfo(tDir);
- DirectoryInfo[] subDir = fDir.GetDirectories();
- FileInfo[] sFile = fDir.GetFiles();
- if (SubFolder)
- {
- foreach (DirectoryInfo d in subDir)
- {
- FileList.AddRange(getFiles(tDir + "" + d.Name, SubFolder));
- }
- }
- foreach (FileInfo f in sFile)
- {
- FileList.Add(f);
- }
- return FileList;
- }
下一步, 就是对取得的文件列表进行操作了,新文件夹自动取名为XXX_NEW, 并且对文件列表按规则进行操作, 即是只解密PHP文件, 还是要同时复制其他非PHP文件:
- private void button1_Click_1(object sender, EventArgs e)
- {
- if (!Directory.Exists(txtFolder.Text)||(txtFolder.Text.IndexOf(" ")>0))
- {
- MessageBox.Show("Directory "" + txtFolder.Text + "" is invalid! (No space allowed)", "Invalid folder selection", MessageBoxButtons.OK,MessageBoxIcon.Error);
- return;
- }
- RootDir = txtFolder.Text;
- string newDir=RootDir + "_new";
- List<FileInfo> FileList = new List<FileInfo>();
- FileList = getFiles(txtFolder.Text, WithSub);
- for (int i = 0; i < FileList.Count; i++)
- {
- string tNewDir = FileList[i].DirectoryName.Replace(RootDir, newDir);
- if (!Directory.Exists(tNewDir))
- {
- Directory.CreateDirectory(tNewDir);
- }
- if (FileList[i].Extension != ".php")//&&(FileList[i].
- {
- File.Copy(FileList[i].DirectoryName + "" + FileList[i].Name, tNewDir + "" + FileList[i].Name);
- txtLog.AppendText("COPIED! " + i.ToString() + " - " + FileList[i].DirectoryName + "" + FileList[i].Name + "nn");
- }
- else
- {
- if (runphp(FileList[i].DirectoryName + "" + FileList[i].Name, tNewDir))
- {
- txtLog.AppendText("DECRYPTED! " + i.ToString() + " - " + FileList[i].DirectoryName + "" + FileList[i].Name + "nn");
- }
- else
- {
- txtLog.AppendText("FAILED TO DECRYPT! " + i.ToString() + " - " + FileList[i].DirectoryName + "" + FileList[i].Name + "nn");
- }
- }
- }
- }
这是解码PHP文件, 通过建立新进程来调用外部EXE文件:
- private bool runphp(string filename, string NewFolder)
- {
- try
- {
- string EXE_PATH = @".";
- System.Diagnostics.Process process = new System.Diagnostics.Process();
- process.StartInfo.FileName = "php.exe";
- process.StartInfo.WorkingDirectory = EXE_PATH;
- process.StartInfo.CreateNoWindow = true;
- process.StartInfo.Arguments = filename + " /tab /indent:1 /path:" + NewFolder + " /ext:* /noexpire";
- process.Start();
- if (process.HasExited)
- {
- //txtLog.AppendText("finished!nn");
- //return true;
- }
- return true;
- }
- catch (Exception e)
- {
- throw e;
- //return false;
- }
- }
其他的代码就不一一罗列了. 只是临时使用的代码, 功能还非常简单.





