C#获取dll自身路径

            //获取dll的绝对路径,请根据不同情况自己选用
            MessageBox.Show(System.Reflection.Assembly.GetExecutingAssembly().Location);
            MessageBox.Show(System.Reflection.Assembly.GetEntryAssembly().Location);   
            MessageBox.Show(System.Windows.Forms.Application.ExecutablePath);
            MessageBox.Show(System.Windows.Forms.Application.StartupPath);
            MessageBox.Show(System.Environment.CurrentDirectory);
            MessageBox.Show(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName);

C#枚举窗口句柄

在CS程序中启动其他应用后,要获取进程的主窗体其实很简单:

        Process p = Process.Start(exePath);
        //p.WaitForInputIdle();
        p.Refresh(); 
        IntPtr mainWnd = p.MainWindowHandle;

但是,总有很多特殊的情况,上面的方法根本无法用,所以,要用Windows API来搞定了

1、如果窗口信息很固定而且没有重名的话,可以用Findwindow搞定

        [DllImport("User32.dll", EntryPoint = "FindWindow")]
        public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
        IntPtr clientWnd = FindWindow(null,"FormClient");

2、根据标题枚举窗口句柄

        //枚举窗体
        [DllImport("User32.dll", EntryPoint = "EnumWindows", SetLastError = true)]
        public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, uint lParam);
        //获取窗体标题
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpText, int nCount);
        //设置错误状态
        [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
        public static extern void SetLastError(uint dwErrCode);

        //线程主窗口句柄
        private static IntPtr processMainWnd = IntPtr.Zero;
        //要查找的窗口名称
        private static String winTitle = "__Web__Form__Main__";

        //声明委托函数
        public delegate bool WNDENUMPROC(IntPtr hwnd, uint lParam);

        //枚举进程句柄,非线程安全
        [SecuritySafeCritical]
        public static IntPtr GetCurrentWindowHandle()
        {
            IntPtr ptrWnd = IntPtr.Zero;

            bool bResult = EnumWindows(new WNDENUMPROC(EnumWindowsProc), (uint)0);
            if (!bResult && Marshal.GetLastWin32Error() == 0)
            {
                ptrWnd = processMainWnd;
            }

            return ptrWnd;
        }

        //枚举函数,获取主窗口句柄然后退出
        [SecuritySafeCritical]
        private static bool EnumWindowsProc(IntPtr hwnd, uint lParam)
        {
            //根据标题获取窗体
            var sb = new StringBuilder(50);
            GetWindowText(hwnd, sb, sb.Capacity);

            if (winTitle.Equals(sb.ToString()))
            {
                processMainWnd = hwnd;
                SetLastError(0);
                return false;
            }

            return true;
        }

3、跟进进程id枚举获取主窗体句柄

        //枚举窗体
        [DllImport("User32.dll", EntryPoint = "EnumWindows", SetLastError = true)]
        //获取父窗体
        [DllImport("user32.dll", EntryPoint = "GetParent", SetLastError = true)]
        //根据窗口获取线程句柄
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowThreadProcessId(IntPtr hwnd, out uint pid);
        //设置错误状态
        [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
        public static extern void SetLastError(uint dwErrCode);

        //线程主窗口句柄
        private static IntPtr processMainWnd = IntPtr.Zero;

        //枚举进程句柄
        [SecuritySafeCritical]
        public static IntPtr GetCurrentWindowHandle(int processId)
        {
            IntPtr ptrWnd = IntPtr.Zero;

            bool bResult = EnumWindows(new WNDENUMPROC(EnumWindowsProc), (uint)processId);
            if (!bResult && Marshal.GetLastWin32Error() == 0)
            {
                ptrWnd = processMainWnd;
            }

            return ptrWnd;
        }

        //枚举函数,获取主窗口句柄然后退出
        [SecuritySafeCritical]
        private static bool EnumWindowsProc(IntPtr hwnd, uint lParam)
        {
            uint uiPid = 0;
            //根据启动方式不同,这里要有相应修改
            if (GetParent(hwnd) == IntPtr.Zero)
            {
                GetWindowThreadProcessId(hwnd, out uiPid);
                if (uiPid == lParam)
                {
                    processMainWnd = hwnd;
                    SetLastError(0);
                    return false;
                }
            }
        }

4、跟进窗口WindowsClassName举获取主窗体句柄

        //查找桌面
        [DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)]
        public static extern IntPtr GetDesktopWindow();
        //获取窗体句柄
        [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern IntPtr GetWindow(IntPtr hwnd, int wFlag);
        //获取窗体类
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

        public const int GW_CHILD = 5;
        public const int GW_HWNDNEXT = 2;

        //枚举桌面窗口,根据WindowsClassName获取句柄
        [SecuritySafeCritical]
        public static IntPtr GetWinHandleByClasName(String windowClassNmae)
        {   
            //取得桌面窗口,再获取第一个子窗口
            IntPtr hwnd = GetDesktopWindow();
            hwnd = GetWindow((IntPtr)hwnd, System.Convert.ToInt32(GW_CHILD));
            
            //通过循环来枚举所有的子窗口
            while ((long)hwnd != 0)
            {
                System.Text.StringBuilder ClassName = new System.Text.StringBuilder(256);
                GetClassName((IntPtr)hwnd, ClassName, ClassName.Capacity);
                String tmpClassName= ClassName.ToString().Trim();
                if (tmpClassName.Length > 0)
                {
                    if (tmpClassName.Equals(windowClassNmae))
                    {
                        break;
                    }
                }

                hwnd = GetWindow((IntPtr)hwnd, System.Convert.ToInt32(GW_HWNDNEXT));
            }

            return hwnd;
        }

使用WebBrower修改网页iFrame中的内容

由于很变态的中国式需求,我们需要在自己的网站中,嵌入第三方厂商的网站。
而这个第三方的网站中,使用了几层的iFrame,同时使用了基于IE的Activex控件。
由于双方的控件风格差距太大,所以需要替换CSS样式,同时要删掉部分与我们网站冲突的内容。

想了几种方法:
1、说服对方进行调整,但经过接触,了解到其超级不靠谱,只能靠自己了。
2、直接通过iFrame嵌入对方网站,用JS进行修改。可是跨站修改,浏览器不同意啊。
3、自己写个控件,嵌入到自己网页中,在控件中调用对方URL,并通过WebBrowser控件修改CSS及HTML。问题是,IE中,不能再直接嵌入一个WebBrowser控件,主要出于安全考虑。(如果IE允许这样操作的话,我只需要在WebBrowser中指向同一个URL,就可以无限调用,耗光资源,其他的事情,有太多可以做了)自己用很挫的方法实现了这个功能,后来考虑到稳定性,放弃了。
4、使用非基于IE的Web插件,在控件中调用对方URL,并通过控件修改CSS及HTML。比如Flash或者CefSharp,但问题是,非IE核心如何调用Activex控件啊。
5、限制入口,基于WebBrowser定制IE浏览器

最后使用了方法5,说实话,真烦。

这里把通过WebBrowser修改CSS及HTML的方法说一下,下面是我写的一段测试用代码,请按自己的实际情况修改:

        //替换样式
        //webMain是WebBrowser控件的名称
        //NewStyle.css中是要替换的样式
        private void modifyStyle()
        {
            int frameNum = webMain.Document.Window.Frames.Count;
            if (frameNum == 2)
            {
                IHTMLDocument2 leftIFrame = webMain.Document.Window.Frames[0].Document.DomDocument as IHTMLDocument2;

                int length = leftIFrame.styleSheets.length;
                IHTMLStyleSheet styleSheet = leftIFrame.createStyleSheet(@"", length + 1);

                TextReader reader = new StreamReader(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "NewStyle.css"));
                string style = reader.ReadToEnd();
                styleSheet.cssText = style;
            }
        }

        //删除tree节点下的div,只保留特定的一个节点
        private void delDiv(String flowid)
        {
            int frameNum = webMain.Document.Window.Frames.Count;
            if (frameNum == 2)
            {
                IHTMLDocument3 leftIFrame = webMain.Document.Window.Frames[0].Document.DomDocument as IHTMLDocument3;
                foreach (dynamic e in leftIFrame.getElementById("tree").children)
                {
                    String ret = e.InnerText;
                    if (!ret.Contains(flowid))
                    {
                        e.parentNode.removeChild(e);
                    }
                }
            }
        }

我可怜的Nexus7平板啊

想来Nexus7已经挂了4、5个月了吧,今年我的电子产品挂得真是干脆,各种悲剧啊。

其实起因很简单,我从广州回上海时,没有带Nexus7。

当我重新回到广州后,发现没什么电了,但可以更新,于是点击了更新。

更新后,无论如何,Nexus7都会卡在Google的启动界面。

当时心情正郁闷而且Nexus7很久没有Root过了,于是准备开始Root。

网上下了最新的Root工具,然后开始Root。

但过了好久都没有任何反应。

立即清楚,这次悲剧了。

重启后,发现Bootloader已经没有了。

网上各种查找资料,发现Nexus7的1代,这个问题我不是第一个遇到,但大家的解决方法,都是“返厂”。

老子代购的,怎么“返厂”啊。

于是在广州、上海找了不少人帮忙看,发现,我的刷机水平,比他们中95%的人都强。
要是能进入fastboot,我找你干吗?

后来,有人说刷字库可以解决问题,找了两家试试,甚至发快递到深圳修,发现都搞不定。

唉。。。

非主流的悲剧啊。。。

估计是没什么希望了。。。

刷机有风险,入行需谨慎。。。

如何在WinForm程序中测试C#写的COM控件

在网上查了一下,应该是不能直接在WinForm中使用C#写的COM控件。
但可以用变通的方法测试COM控件中的方法,比如,下面的例子中,就可以测试addTest方法:

Type comType = null;
object comObject = null;

//这个无效,不知道为什么
//comType = Type.GetTypeFromProgID("XX.CSControl.TestControl");
if (comType == null)
{
    Guid guid = new Guid("4B4D1D4C-16CA-48E0-87A4-AFF3C6CB6E26");
    comType = Type.GetTypeFromCLSID(guid);
}

try
{
    comObject = Activator.CreateInstance(comType);
}
catch(Exception e)
{
    MessageBox.Show(e.Message);
    return;
}

object[] args = new object[2];
args[0] = 1;
args[1] = 2;

int ret = (int)comType.InvokeMember("addTest", BindingFlags.InvokeMethod, null, comObject, args);
if(ret==3)
{
    MessageBox.Show("TestOK");
}

C#开发COM组件(Web)

这两天由于一个很挫的需求,准备开发一个COM组件,由于所有用户的客户端都安装有.Net Framework 4.0,加上工期紧的很,就准备用C#上了。

1、首先,建立一个Windows Forms Control Library项目

2、项目属性,Build,选中,Register for COM interop

3、项目属性,Signing,选中,Sign the assembly,并生成一个签名文件

4、编辑AssemblyInfo.cs,查看是否有下面两行,没有要添加

using System.Security;

[assembly: AllowPartiallyTrustedCallers()] 
[assembly: ComVisible(true)]

5、添加接口文件IObjectSafety.cs

using System;
using System.Runtime.InteropServices;

namespace CSControl
{
    //不要修改GUID
    [ComImport]
    [Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IObjectSafety
    {
        [PreserveSig]
        int GetInterfaceSafetyOptions(ref Guid riid,
                      [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions,
                      [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);

        [PreserveSig()]
        int SetInterfaceSafetyOptions(ref Guid riid,
                      [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask,
                      [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);
    }
}

6、控件类要同时实现UserControl, IObjectSafety两个接口

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Security;

namespace CSControl
{
    //更改为你自己的UUID
    [ComVisible(true)]
    [Guid("4B4D1D4C-16CA-48E0-87A4-AFF3C6CB6E26")]
    [ProgId("XX.CSControl.TestControl")]
    public partial class TestControl : UserControl, IObjectSafety
    {
        //===================================================
        //实现IObjectSafety接口
        //不要修改下面的GUID
        private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
        private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
        private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
        private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
        private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";

        private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
        private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
        private const int S_OK = 0;
        private const int E_FAIL = unchecked((int)0x80004005);
        private const int E_NOINTERFACE = unchecked((int)0x80004002);

        private bool _fSafeForScripting = true;
        private bool _fSafeForInitializing = true;

        public int GetInterfaceSafetyOptions(ref Guid riid,
                             ref int pdwSupportedOptions,
                             ref int pdwEnabledOptions)
        {
            int Rslt = E_FAIL;

            string strGUID = riid.ToString("B");
            pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
            switch (strGUID)
            {
                case _IID_IDispatch:
                case _IID_IDispatchEx:
                    Rslt = S_OK;
                    pdwEnabledOptions = 0;
                    if (_fSafeForScripting == true)
                        pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
                    break;
                case _IID_IPersistStorage:
                case _IID_IPersistStream:
                case _IID_IPersistPropertyBag:
                    Rslt = S_OK;
                    pdwEnabledOptions = 0;
                    if (_fSafeForInitializing == true)
                        pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
                    break;
                default:
                    Rslt = E_NOINTERFACE;
                    break;
            }

            return Rslt;
        }

        public int SetInterfaceSafetyOptions(ref Guid riid,
                             int dwOptionSetMask,
                             int dwEnabledOptions)
        {
            int Rslt = E_FAIL;

            string strGUID = riid.ToString("B");
            switch (strGUID)
            {
                case _IID_IDispatch:
                case _IID_IDispatchEx:
                    if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) &&
                         (_fSafeForScripting == true))
                        Rslt = S_OK;
                    break;
                case _IID_IPersistStorage:
                case _IID_IPersistStream:
                case _IID_IPersistPropertyBag:
                    if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) &&
                         (_fSafeForInitializing == true))
                        Rslt = S_OK;
                    break;
                default:
                    Rslt = E_NOINTERFACE;
                    break;
            }

            return Rslt;
        }

        //===================================================
        //实现UserControl接口
        ......
    }
}

7、编译,控件会自动注册

8、写个网页,测试一下

<!--CSControl-->
<object id="testControl" width="500" height="500" classid ="clsid:4B4D1D4C-16CA-48E0-87A4-AFF3C6CB6E26"&#93;</object>

9、打CAB包,发布到网站
由于是自己生产的签名文件,所以会有安全提示。最简单的方法是从靠谱的CA那里买一个证书,还有办法就是将证书导入到IE的信任列表中,再就是将IE的安全级别降为最低。

10、当然也可以通过命令行本地注册

rem 注册
regtlibv12 %PATH_TO_DLL_AND_TLB_FOLDER%\CSControl.tlb

rem 反注册
regtlibv12 -u %PATH_TO_DLL_AND_TLB_FOLDER%\CSControl.tlb

C#开发COM组件(本地)

本篇文章介绍了如何用C#写一个简单的COM组件(Dll)。

1、新建一个C# Class Library项目“CSCOMTest”

2、项目中,新增一个接口文件IJustATestCOM.cs,Guid要自己生成

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace CSCOMTest
{
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("A5377871-3334-4679-A3CD-84312B1DCD3E")]
    public interface IJustATestCOM
    {
        int Add(int a, int b);
        String SayHiTo(String someOne);
    }
}

3、项目中,新增一个类文件JustATestCOM.cs,Guid要自己生成

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace CSCOMTest
{
    [ComVisible(true)]
    [Guid("A6E4F456-32C1-4C8E-9171-D616B5DA1E20")]
    [ProgId("CSCOMTest.JustATestCOM")]
    public class JustATestCOM : IJustATestCOM
    {
        public int Add(int a, int b)
        {
            return a + b;
        }

        public String SayHiTo(String someOne)
        {
            return "Hi " + someOne + "!";
        }
    }
}

4、项目属性->Application->Assembly Infomation->Make Assembly COM-Visible->打勾

5、项目属性->Signing->Sign the assembly->打勾
项目属性->Signing->Choose a strong name key file->New

6、编译

7、注册

#这里一定要用正确版本的gacutil及RegAsm
C:\VBS\DNFW4\gacutil.exe /i CSCOMTest.dll
C:\VBS\DNFW4\RegAsm.exe CSCOMTest.dll

8、反注册

#这里一定要用正确版本的gacutil及RegAsm
C:\VBS\DNFW4\gacutil.exe /u CSCOMTest
C:\VBS\DNFW4\RegAsm.exe /u CSCOMTest.dll

9、调用测试

'发生错误时,继续运行
On Error Resume Next

'清除错误状态
Err.Clear

Set Obj=CreateObject("CSCOMTest.JustATestCOM")

'输出错误信息
If Err.Number <> 0 Then
    WScript.Echo "Error: " & Err.Number
    WScript.Echo "Error (Hex): " & Hex(Err.Number)
    WScript.Echo "Source: " &  Err.Source
    WScript.Echo "Description: " &  Err.Description
    'Err.Clear
    '退出程序
    WScript.Quit(Err.Number)
End If

'On Error Goto 0

WScript.Echo obj.Add(1,2)
WScript.Echo obj.SayHiTo("dcom")


set obj=Nothing

10、生成tlb文件

regasm CSCOMTest.dll /tlb:CSCOMTest.tlb

iPhone4数据备份成功

//=========================================================
上周在天津,找了个iPhone的修理店,里面的小师傅说我的iPhone4主板短路了,不能修。

我和他们商量,帮我换块电池吧,结果被一大三小四个师傅一起鄙视,说是会引起火灾。

估计到最后他们也没明白,我只是想备份数据,手机不打算用了。

但人家说就是不可能开机了,靠,鄙视,有电明明能开机的。

没办法,自己X宝买了个电池。

//=========================================================
昨天,终于回到上海。

电池早就到了,于是下班的时候,换上新电池,开始了手机数据备份。。。

换了电池,开机,成功了!

哈哈哈哈哈。。。

马上备份。。。

然后手机开始发烫,关到飞行模式也不行。。。

只好备份一部分就关机散热,中途自己一顿人工降温。。。

弄了半个小时,终于备份完成了。。。

//=========================================================
后记:
昨天晚上去买5S,晚上去中山公园的永乐买,结果没32G的,就跑到了陆家嘴Apple旗舰店。
买是买到了,但发现,卡必须是nano的,没有卡没法激活。
靠。。。
再等等吧。。。

//=========================================================
卡激活了,还是去联通剪的卡,还在下雨,好坑的说
终于用上了:)

//=========================================================
鄙视Apple,刚买了不到一个月,降价了1200,一张机票没了:(

获取Eclipse执行文件根目录

Eclipse插件获取Eclipse的根目录

String eclipseRoot = Platform.getInstallLocation().getURL().toString();
eclipseRoot = eclipseRoot.replace("file:/", "");

Eclipse插件获取Workspace根目录

//方法1
String workspaceRoot= Platform.getInstanceLocation().getURL().toString();
workspaceRoot = workspaceRoot.replace("file:/", "");

//方法2
String path = Activator.getDefault().getStateLocation().makeAbsolute().toFile().getAbsolutePath();

Eclipse插件获取User根目录

String userHome = Platform.getUserLocation().getURL().toString();
userHome = userHome.replace("file:/", "");

Elclipse插件获取Bundle的OSGI路径

//方法1
String bundlePath = Activator.getDefault().getBundle().getLocation();
String pathBundle = bundlePath.replace("reference:file:/","");

//方法2
Bundle bundle = Platform.getBundle("bundle id");
URL urlentry = bundle.getEntry("bundle resource path");
String strEntry = FileLocator.toFileURL(urlentry).getPath();

//方法3
String pathClass = KeyHandler.class.getResource("resource path").getFile();