存档

文章标签 ‘C#’

win32 程序窗体居中

2015年12月20日 没有评论

C#对于窗体居中很简单,只需要简单设置一个属性就可以了,但是对于C++,还需要额外的写点代码。下面是衣服自己洗分享的代码,可以直接拷贝到项目中。

inline static BOOL CenterWindow(HWND hwndWindow,bool isDesktopParent = true)
{
HWND hwndParent;
RECT rectWindow, rectParent;

hwndParent = isDesktopParent ? GetDesktopWindow() : GetParent(hwndWindow);

//make the window relative to its parent
if (hwndParent != nullptr)
{
GetWindowRect(hwndWindow, &rectWindow);
GetWindowRect(hwndParent, &rectParent);

int nWidth = rectWindow.right – rectWindow.left;
int nHeight = rectWindow.bottom – rectWindow.top;

int nX = ((rectParent.right – rectParent.left) – nWidth) / 2 + rectParent.left;
int nY = ((rectParent.bottom – rectParent.top) – nHeight) / 2 + rectParent.top;

int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);

//make sure the window never moves outside of the screen
if (nX < 0) nX = 0;
if (nY < 0) nY = 0;
if (nX + nWidth > nScreenWidth) nX = nScreenWidth – nWidth;
if (nY + nHeight > nScreenHeight) nY = nScreenHeight – nHeight;

MoveWindow(hwndWindow, nX, nY, nWidth, nHeight, FALSE);

return TRUE;
}

return FALSE;
}

分类: C++, 一句话 标签: ,

jsoncpp解决中文乱码问题

2015年12月13日 没有评论

衣服自己洗使用jsoncpp来解析项目中使用的json字符串,后来发现一个问题就在于jsoncpp 不支持unicode编码的中文字符。

网上搜索了一下,一种比较弱侵入性的方法如下:

打开json_tool.h文件,找到 codePointToUTF8 方法,修改 else if (cp <= 0xFFFF) 代码段里的内容,添加额外的处理。

//添加中文unicode编码
if((cp >= 0x4E00 && cp <= 0x9FA5) || (cp >= 0xF900 && cp <= 0xFA2D))
{
wchar_t src[2] = { 0 };
char dest[5] = { 0 };
src[0] = static_cast<wchar_t>(cp);
std::string local = setlocale(LC_ALL, NULL);
setlocale(LC_ALL, “chs”);
wcstombs_s(NULL, dest, 5, src, 2);
result = dest;
setlocale(LC_ALL, local.c_str());
}
//下面 else 里代码为原始代码
else
{
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
}

然后重新编译即可。

分类: C++, 一句话 标签: , ,

解决spdlog无法编译的问题

2015年11月29日 没有评论

衣服自己本来是想使用log4cxx\log4cpp\log4cplus这样的库来作为日志库的,但是无论那个都无法集成,一堆的问题,于是衣服自己洗一番搜索后,发现spdlog 是一个速度非常快的C++开源库,线程安全。

这个库有一个特点就是全部都是头文件,没有cpp文件,直接引用即可。

使用VS2015添加了include文件引用后,编译报错。

错误 C2589 “(”:“::”右边的非法标记

参考了一下,发现是和系统的命名冲突,解决方法也很简单。

打开 format.h 文件,找到 int compare(BasicStringRef other) const 方法,将 std::size_t size = std::min(size_, other.size_) 修改为 std::size_t size = (std::min)(size_, other.size_),然后重新编译即可。

分类: C++, 日常 标签: ,

c#代码创建快捷方式

2015年5月12日 没有评论

在前面的文章中,衣服自己洗分享了如何附加程序到任务栏。不过首先,你得有一个快捷方式,我们这里就来分享如何使用c#创建快捷方式。

C#里没有直接创建快捷方式的方法,衣服自己洗也在网上搜索了下,除了本文的代码外,还有利用WHO和vbs的方式,本文是使用直接代码com调用的方式,所谓萝卜白菜各有所爱,大家看着来吧。
代码比较长,总的来说,就是定义了2个结构体,一个类,一个接口,然后就是供外部调用的方法了。这里给出了关键的代码,剩余部分代码见下面的截图。

public static bool CreateShortcut(string shortcutPath, string targetPath, string workingDirectory, string description, string iconLocation = null)
{
const int SW_SHOWNORMAL = 1;
try
{
CShellLink cShellLink = new CShellLink();

IShellLink iShellLink = (IShellLink)cShellLink;
iShellLink.SetDescription(description);
iShellLink.SetShowCmd(SW_SHOWNORMAL);
iShellLink.SetPath(targetPath);
iShellLink.SetWorkingDirectory(workingDirectory);

if (!string.IsNullOrEmpty(iconLocation))
{
iShellLink.SetIconLocation(iconLocation, 0);
}

IPersistFile iPersistFile = (IPersistFile)iShellLink;
iPersistFile.Save(shortcutPath, false);

Marshal.ReleaseComObject(iPersistFile);
iPersistFile = null;
Marshal.ReleaseComObject(iShellLink);
iShellLink = null;
Marshal.ReleaseComObject(cShellLink);
cShellLink = null;

return true;
}
catch //(System.Exception ex)
{
return false;
}
}

shortcut

分类: 一句话 标签: , ,

c#获取可执行代码文件的数字签名信息

2015年5月12日 没有评论

在最近的场景中,有遇到过在运行某个程序前,先对程序进行数字签名验证。如果通过则运行,否则不运行。
数字签名技术广泛用在 exe、dll 等二进制可执行文件中,防止文件被篡改,保证了文件的唯一性。一旦文件被修改,则数字签名丢失。

C#中对获取数字签名基本信息是很简单的,微软已经帮我们封装好了,首先添加引用using System.Security.Cryptography.X509Certificates;
然后就是普通的调用:

X509Certificate cert = X509Certificate.CreateFromSignedFile(path);
var s = cert.Subject;
MessageBox.Show(s);

效果如下所示:

cert

certdlg

当然,这只是一个初步的校验,即校验是否存在数字签名。至于数字签名是否合法则需要进一步去做校验。另外,对于CreateFromSignedFile方法,如果文件没有数字签名,那么会引发异常,所以代码里应该添加对该异常的处理。

分类: 一句话 标签: , ,

win7/8/8.1中c#附加程序到任务栏

2015年5月12日 没有评论

远的不说,自从win7开始,当应用程序可以被附加到任务栏后,任务栏就成为各个应用抢占的入口,这里可以比开始菜单更快地启动程序。后来听说微软对OEM厂商有要求,预装软件不能随便pin到任务栏上,哈哈。

今天衣服自己洗就来分享如何附加程序到任务栏上。

大致的流程是先创建一个快捷方式,然后对该快捷方式执行附加命令。附加完成后,可以删除该快捷方式,任务栏图标还是存在的。对于取消是一样的步骤,无非就是步骤不一样而已。当附加成功后,会在用户的Roaming相关目录下自动生成快捷方式,系统会使用该快捷方式,而不是前面创建的快捷方式。

命令就是动作谓词,常见的谓词有run、open等操作,而附加和取消的谓词就是TaskbarPin/TaskbarUnPin,从实际经验来看,谓词是不区分大小写的。代码很简单,看看就明白了。
先申明操作的返回值枚举

public enum FileResult
{
Success,Failed,Exist,NotExist
}

附加到任务栏

public static FileResult PinToTaskBar(string linkPath, bool deleteLink = false)
{
if (!File.Exists(linkPath))
{
// 快捷方式不存在
return FileResult.Failed;
}

string name = System.IO.Path.GetFileName(linkPath);
string userPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile, Environment.SpecialFolderOption.None)
+”\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar\\”;

if (File.Exists(userPath + name))
{
//当pin到任务栏后,该目录下会生成对应的快捷方式。
//通过判断该文件是否存在判断是否已经pin到任务栏
return FileResult.Exist;
}

ProcessStartInfo psi = new ProcessStartInfo(linkPath);

psi.Verb = “TaskbarPin”;
Process.Start(psi);

if (deleteLink)
{
//pin 完后删除快捷方式
File.Delete(linkPath);
}

return FileResult.Success;
}

取消附加到任务栏

public static FileResult UnPinToTaskBar(string linkPath, bool deleteLink = false)
{
if (!File.Exists(linkPath))
{
// 快捷方式不存在
return FileResult.Failed;
}

string name = System.IO.Path.GetFileName(linkPath);
string userPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile, Environment.SpecialFolderOption.None)
+ “\\AppData\\Roaming\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar\\”;

if (!File.Exists(userPath + name))
{
//当pin到任务栏后,该目录下会生成对应的快捷方式。
//通过判断该文件是否存在判断是否已经pin到任务栏
return FileResult.NotExist;
}

ProcessStartInfo psi = new ProcessStartInfo(linkPath);

psi.Verb = “TaskbarUnPin”;
Process.Start(psi);

if (deleteLink)
{
//pin 完后删除快捷方式
File.Delete(linkPath);
}

return FileResult.Success;
}

完整的代码如下图所示。

pin2TaskBar

 

在执行附加操作的时候,如果任务栏上已经附加了,执行谓词命令就会失败。同理,如果任务栏上没有,执行取消附加也会失败,所以就需要做一个判断,只在必要的时候来做判断。并根据不同的情况返回不同的枚举。

分类: 日常 标签: , ,

c++和c#相互调用dll

2015年5月5日 评论已被关闭

前不久昊问起来如何让c++和c#相互调用dll,后来专门去搜索了下,现在衣服自己洗分享出来,可能对大家有帮助。
首先我们来说c#调用c++编译的dll。这个网上已经很多了,c++里和普通写法一样,设置导出函数等等,例如c++里有一个函数 int cplusplus(int a,int b),那么在C#里,可以先申明:

[DllImport (abc.dll)]
public extern static int cplusplus(int a,int b);

然后就可以像普通的C#函数一样调用了。有几个需要注意的地方,
1、DllImport 后括号里输入c++ 的dll 的文件名,如果该dll不是在系统目录或者环境变量的目录,或者是c#程序的bin目录里,这里就应该输入完整的路径。建议放到c#的bin目录里。
2、函数必须声明为public的,不要忘记了extern关键字,当然static也所必需的。
反过来,c++是如何调用c#的dll呢,例如c#里有一个函数 public int csharp(int a,int b)在类Pipe下,而Pipe类属于ABC命名空间,那么对于c++项目,首先应该设置项目属性,打开公共语言运行时支持,即/clr标记,然后c++项目中添加c#的dll文件引用,接下来,在cpp文件里添加c#对应函数的命名空间 using namespace ABC;最后就是在需要调用的地方,使用托管代码的方式调用:

Pipe ^p = gcnew Pipe();
int a = p.csharp(3,4);

网上有同学提及编译失败,有一种可能是没有添加dll的引用,如果不喜欢添加引用,那么在cpp 文件里,使用 #using “..\Debug\csharp.dll” 也是一样可以的,不过这里就是用using,而非 include关键字,当然绝对路径也是不可少的。

现在c++和c#相互调用dll的方式就算完成了,有的同学对c++调用c#的dll的方式不太认可。为什么呢,因为要用托管代码的方式。其实上面描述的方法,本质上是使用的COM调用来完成的,微软也给出了直接COM调用的示例,喜欢这种方式的同学不妨琢磨下。

分类: C++, 日常 标签: , ,

树莓派/香蕉派语音合成

2015年3月3日 没有评论

自从衣服自己洗入手了香蕉派(banana pi)后,已经过去了很长时间了。
其实这些时间里,一直在解决语音合成的问题。按照当时的初衷是想做一个有点智(dou)能(bi)味道的东西。在olay怀孕期间,做了一个定时任务,每天定时播放胎教音乐,不过现在看起来小红薯似乎对音乐也没有多大兴趣。

然后第二步是打算做天气的定时语音预报,一直比较中意的是科大讯飞的语音合成技术。衣服自己洗本来就对Linux编译特别不熟悉,在他们的开放平台上申请了个appid后,就走上了自我折磨的不归路哇。

由于香蕉派上安装的是Lubuntu系统,硬件配置不算好,所以打算在电脑上把代码编译后再复制到香蕉派上。科大讯飞官方提供的SDK,并没有c#的版本,就考虑用c++编译一个仅仅负责语音合成的程序,然后用c#来写逻辑。Java什么的还是算了吧,看到java一大堆的配置就头疼。

mono安装下来,跑了个命令行似乎没有什么问题,于是开始处理语音合成部分。装了个Ubuntu虚拟机,编译官方的demo后拷贝到香蕉派上,运行失败。突然想起来banana pi是基于arm系统,而虚拟机里的ubuntu是基于x86的。

那么退一步,直接在香蕉派上编译应该就可以了吧。
于是直接远程桌面香蕉派,安装了CodeLite开发环境,上传源码,编译失败,一番搜索发现是要添加 -ldl 选项,但是一看官方的build脚本,是有这个选项的。后来各种尝试后,万万没有想到,应该把 -ldl 放到编译命令的最后面。当然咯,-pthread 也是一样的道理,这里就不多说了。

坑的眼睛都睁不开。

修改了编译脚本后,可以正常编译了,但是运行失败,估计是官方提供的 libmsc.so 库格式不对。
于是发邮件,对方说需要提供appid和交叉编译的名称,于是又网上搜索交叉编译,发现大家用的基本上是arm-linux-gcc,回邮件。一周后,交叉编译提供的 libmsc.so 库发过来,重新引用编译。
运行提示找不到 libmsc.so 库。再次网络搜索,发现Linux系统中对于共享库文件默认是在固定的几个目录里,而不像windows那样,先从程序的运行目录里查找dll文件。添加进去还是不行。接着网络搜索,发现还要设置 export LD_LIBRARY_PATH 为库文件存放的目录,同时还需要sudo ldconfig -v来刷新。

多么纠结的Linux啊~泪流满面。喔,忘记说的是,衣服自己洗还不会C++,为了这事情,又买了C++开发的书来学习,精神可嘉有没有~

运行还是提示找不到libmsc.so文件,官方论坛里也没有有效的答复,最后我放弃了。

今天在论坛里发现官方释放了一个针对树莓派的demo,但是提供的是一个静态库a文件。抱着试一试的想法,上传,编译失败。网上搜索也没有个所以然出来。照葫芦画瓢自己写编译脚本运行还是不行。突然想起来前面的 -ldl 要放到最后面的问题,于是添加到最后。终于编译成功,运行成功。

很是激动有没有,这就意味着困扰了好几个月的问题终于解决了。后面就好说了,获取天气预报,用c#来处理,这个实现,就放到后面的博客里描述吧。

c#创建大于屏幕尺寸的窗体

2014年11月16日 没有评论

最近的一个星期,衣服自己洗一直在折腾一件事情,最终才得以解决,现在拿出来和大家分享。

问题是这样的,如何生成一个winform 窗体,使得它的尺寸要比当前屏幕还要大。

我是在 c# 中碰到的,但是对于其它语言应该也是一样的。开始的时候我也为是MDI的限制,后来发现这个限制对一般窗体都适用,网上资料也显示,对于窗体的尺寸设置,无论是 Size 还是单独的宽度、高度,如果超过了屏幕的尺寸,系统会自动给截断。

例如,对于1366*768 分辨率、100%的DPI,如果你创建了一个窗体,并设置其尺寸为 1400*900,那么最终的窗体尺寸会是 1478*780,多余的就不显示了,当然也不会崩溃。系统对于宽度和高度会允许多添加 12像素。如果是 125%的DPI,那么这个值是 16像素。但是不会允许更多。

由于对于windows桌面应用程序驾驭能力的不足,只好请教他人。对于这个问题,大家集中表现为两种情况,一种是表示从来没有接触过MDI表示不会,毕竟是从MDI程序发现的;另外的尝试一把发现确实不太好解决劝我放弃。只好自己一个人坚持下去寻找原因。

我相信这个需求一定是可以实现,并且很简单,只是我暂时没有找到方法而已。

后来看到一个Delphi的程序实现了我想要的效果,我通过spy++比较程序间的差异,并辗转和人家交流才明白,原来是要去掉一个样式并重新设置大小。但是我这边一直没有效果。后来才发现是常量声明错误,我给设置成负值了。汗~

首先,获取窗体的当前样式,然后去掉WS_THICKFRAME样式,之后再重新设置窗体的位置和尺寸即可。如我开始所想,确实很简单,只是想到这里不太容易。如果是 c++ 之类的语言,可以在窗体(包括MDI窗体)构造的时候设置就可以了,c# 给封装起来了,所以只好放后面设置。

申明如下:

[DllImport(“user32.dll”, SetLastError = true, CharSet = CharSet.Auto)]
public static extern int SetWindowLong(IntPtr hwnd, int nIndex, int newLong);

[DllImport(“user32.dll”, SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetWindowLong(IntPtr hwnd, int nIndex);

[DllImport(“user32.dll”)]
public static extern int SetWindowPos(IntPtr hwnd, int hwndInsertAfter, int x, int y, int width, int height, int flags);

[DllImport(“shell32.dll”)]
public static extern IntPtr ShellExecute(IntPtr hwnd,string lpOperation,string lpFile,string lpParameters,string lpDirectory,int nShowCmd);

public static int GWL_STYLE = (-16);
public static int WS_THICKFRAME = 262144;
public static int HWND_TOP = 0;
public static int SWP_SHOWWINDOW = 64;
public static int SW_SHOW = 5;

封装起来,这样调用会比较方便,
public void SetNewSize(int width, int height)
{
int style = GetWindowLong(this.Handle, GWL_STYLE);

SetWindowLong(this.Handle, GWL_STYLE, style & ~WS_THICKFRAME);
SetWindowPos(this.Handle, HWND_TOP, 0, 0, width, height, SWP_SHOWWINDOW);
}

最后说几个小细节作为结尾吧,对于c#而言,这个调用不能放在构造函数里,因为会被后面系统自动设置给覆盖掉,应该在 frmChild.Show()之后才有效。衣服自己洗这里也是坑了很久。另外大家可以看代码是如何去掉WS_THICKFRAME样式的,还可以思考下为什么要先获取窗体样式而不直接设置。

这下算是解决了心里的一个疙瘩,不知道做技术的是不是都这样。

分类: 日常 标签: , , ,

c# winform添加防火墙规则支持

2014年9月23日 没有评论

在最近的一个项目中,程序需要依赖第三方应用。所以在程序启动的时候,如果第三方应用没有运行,则主动调用起来。对于网络应用程序,或者一些需要用到自定义端口的程序,在启动的时候,可能会被系统防火墙给拦截,询问用户是否需要给与权限。

有的时候,这会让人很抓狂,特别是对于某xx程序,呵呵,我真的不是在黑360。

如果一个已经被用户授权获得管理员权限的应用,如果可以顺手把防火墙规则给加上去,至少看起来不用那么纠结了。

多的不说,下面是具体的静态类。

public class FirewallHelper
{
/// <summary>
/// 添加端口号到防火墙
/// </summary>
/// <param name=”name”></param>
/// <param name=”port”>端口号</param>
/// <param name=”protocol”>TCP/UDP/ANY</param>
public static void addPort(string name, int port, string protocol)
{
//创建防火墙管理类实例
INetFwMgr manager = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromProgID(“HNetCfg.FwMgr”));
//或者使用Type.GetTypeFromCLSID(new GUID(” {304CE942-6E39-40D8-943A-B913C40C9CD4}”)),
//其中, {304CE942-6E39-40D8-943A-B913C40C9CD4} 是防火墙的CLSID

INetFwOpenPort fwPort = (INetFwOpenPort)Activator.CreateInstance(Type.GetTypeFromProgID(“HNetCfg.FwOpenPort”));

fwPort.Name = name;
fwPort.Port = port;
if (protocol.ToUpper() == “TCP”)
{
fwPort.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP;
}
else if (protocol.ToUpper() == “ANY”)
{
fwPort.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_ANY;
}
else
{
fwPort.Protocol = NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP;
}
fwPort.Scope = NET_FW_SCOPE_.NET_FW_SCOPE_ALL;
fwPort.Enabled = true;

bool exist = false;
//加入到防火墙的管理策略
foreach (INetFwOpenPort item in manager.LocalPolicy.CurrentProfile.GloballyOpenPorts)
{
if (fwPort == item)
{
exist = true;
break;
}
}
if (!exist) {
//不存在则添加
manager.LocalPolicy.CurrentProfile.GloballyOpenPorts.Add(fwPort);
}
}

/// <summary>
/// 添加应用程序到防火墙
/// </summary>
/// <param name=”name”></param>
/// <param name=”path”>应用程序的完整绝对路径,包含文件名</param>
public static void addApp(string name, string path)
{
INetFwMgr manager = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromProgID(“HNetCfg.FwMgr”));
INetFwAuthorizedApplication app = (INetFwAuthorizedApplication)Activator.CreateInstance(Type.GetTypeFromProgID(“HNetCfg.FwAuthorizedApplication”));

app.Name = name;
app.ProcessImageFileName = path;
app.Enabled = true;

bool exist = false;
foreach (INetFwAuthorizedApplication item in manager.LocalPolicy.CurrentProfile.AuthorizedApplications)
{
if (name == item.Name)
{
exist = true;
break;
}
}

if (!exist)
{
manager.LocalPolicy.CurrentProfile.AuthorizedApplications.Add(app);
}
}

/// <summary>
/// 从防火墙中删除端口号
/// </summary>
/// <param name=”port”></param>
/// <param name=”protocol”>TCP/UDP/ANY</param>
public static void delPort(int port, string protocol)
{
INetFwMgr manager = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromProgID(“HNetCfg.FwMgr”));

if (protocol.ToUpper() == “TCP”)
{
manager.LocalPolicy.CurrentProfile.GloballyOpenPorts.Remove(port, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_TCP);
}
else if (protocol.ToUpper() == “ANY”)
{
manager.LocalPolicy.CurrentProfile.GloballyOpenPorts.Remove(port, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_ANY);
}
else
{
manager.LocalPolicy.CurrentProfile.GloballyOpenPorts.Remove(port, NET_FW_IP_PROTOCOL_.NET_FW_IP_PROTOCOL_UDP);
}
}

/// <summary>
/// 从防火墙中移除应用程序
/// </summary>
/// <param name=”path”>应用程序的完整绝对路径,包含文件名</param>
public static void delApp(string path)
{
INetFwMgr manager = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromProgID(“HNetCfg.FwMgr”));

manager.LocalPolicy.CurrentProfile.AuthorizedApplications.Remove(path);
}
}

 

因为都是静态类,要使用的时候,直接调用就可以了。

分类: 日常 标签: , ,