Excel催化剂开源第47波-Excel与PowerBIDeskTop互通互联之第一篇

当国外都在追求软件开源,并且在GitHub等平台上产生了大量优质的开源代码时,但在国内却在刮着一股收割小白智商税的知识付费热潮,实在可悲。
互联网的精神乃是分享,让分享带来更多人的受益。
在PowerBI领域,出现了十分优秀的DAXStudio和Tabular Editor等开源工具,影响深远。借此,Excel催化剂也决定将最核心的、也是PowerBI群体中热切盼望到功能点进行开源。
但愿从中受益的群体,不要将其视为其有,并且利用信息不对称继续进行收割智商税的延续,并且最好能够在引用时按开源的原则,署名上代码出处。

此篇对应功能实现出自:第3波-与PowerbiDesktop互通互联(Excel透视表连接PowerbiDesktop数据模型)https://www.jianshu.com/p/e05460ad407d

如何识别到当前电脑打开的PowerBIDeskTop所开启的SSAS服务实例?

因PowerBIDeskTop运行的时候,是开启了SSAS的实例,和Sqlserver上开启的类似,只是功能上受限于本机访问,具体表现形式为在任务管理器上可查看到有msmdsrv.exe进程。

当打开多个pbix文件时,会出现多个msmdsrv.exe进程,而Excel连接PowerBIDeskTop的核心就变为识别到msmdsrv.exe所开启的端口号。

而就算识别到端口号时,如果有多个msmdsrv.exe同时运行,还需要将不同的msmdsrv.exe所开启的端口号,对应回原来的PowerBIDeskTop打开的Pbix文件。

只有将文件名关联进来,在用户查看时,才能分辨出具体哪个msmdsrv.exe端口对应的连接属于哪个模型,最终通过连接所需要的端口号,实现连接到所需要的相应的pbix文件对应的数据模型中来。

具体代码

Excel催化剂实现了以上的技术难点,使用的是DAXStudio开源代码里的代码片段。

老规则,先建立一个实体类,用于存储一些关键信息。

    class PbidFileInfo
    {
        public string FileName { get; set; }
        public int Port { get; set; }
        public string DbName { get; set; }

        public string ModelName { get; set; }

    }

通过以下的代码,对PowerBIDeskTop所开启的SSAS实例的端口号及对应的pbix文件名等信息进行获取,返回List清单。


public static List<Entity.PbidFileInfo> GetPbidPortTittleMappings()
        {
            List<Entity.PbidFileInfo> pbidPortTittleMappings = new List<Entity.PbidFileInfo>();
            ManagementClass mgmtClass = new ManagementClass("Win32_Process");
            foreach (ManagementObject process in mgmtClass.GetInstances())
            {
                string processName = process["Name"].ToString().ToLower();
                if (processName == "msmdsrv.exe")
                {

                    // get the process pid
                    System.UInt32 pid = (System.UInt32)process["ProcessId"];
                    var parentPid = int.Parse(process["ParentProcessId"].ToString());
                    var parentTitle = "";
                    if (parentPid > 0)
                    {
                        var parentProcess = Process.GetProcessById(parentPid);
                        //if (parentProcess.ProcessName == "devenv") _icon = EmbeddedSSASIcon.Devenv;
                        parentTitle = parentProcess.MainWindowTitle;
                        if (parentTitle.Length == 0)
                        {
                            // for minimized windows we need to use some Win32 api calls to get the title
                            parentTitle = GetWindowTitle(parentPid);
                        }
                    }
                    // Get the command line - can be null if we don't have permissions
                    // but should have permission for PowerBI msmdsrv as it will have been
                    // launched by the current user.
                    string cmdLine = null;
                    if (process["CommandLine"] != null)
                    {
                        cmdLine = process["CommandLine"].ToString();
                        try
                        {
                            var rex = new System.Text.RegularExpressions.Regex("-s\\s\"(?<path>.*)\"");
                            var m = rex.Matches(cmdLine);
                            if (m.Count == 0) continue;
                            string msmdsrvPath = m[0].Groups["path"].Captures[0].Value;
                            var portFile = string.Format("{0}\\msmdsrv.port.txt", msmdsrvPath);
                            if (System.IO.File.Exists(portFile))
                            {
                                string sPort = System.IO.File.ReadAllText(portFile, Encoding.Unicode);
                                string cnnString = cnnString = $"localhost:{sPort}";
                                using (AMO.Server server = new AMO.Server())
                                {
                                    server.Connect(cnnString);
                                    string dbName = server.Databases[0].Name;
                                    string modelName = server.Databases[0].Model.Name;

                                    pbidPortTittleMappings.Add(new Entity.PbidFileInfo()
                                    {
                                        FileName = parentTitle.Replace(" - Power BI Desktop", ""),
                                        Port = int.Parse(sPort),
                                        DbName = dbName,
                                        ModelName = modelName
                                    });
                                }
                            }
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }
            return pbidPortTittleMappings;
        }

        //#region PInvoke calls to get the window title of a minimize window
        delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);

        [DllImport("user32.dll")]
        static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("user32.dll")]
        static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn,
            IntPtr lParam);

        private static IEnumerable<IntPtr> EnumerateProcessWindowHandles(int processId)
        {
            var handles = new List<IntPtr>();

            foreach (ProcessThread thread in Process.GetProcessById(processId).Threads)
                EnumThreadWindows(thread.Id,
                    (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);

            return handles;
        }

        private const uint WM_GETTEXT = 0x000D;

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam,
            StringBuilder lParam);

        private static string GetWindowTitle(int procId)
        {
            foreach (var handle in EnumerateProcessWindowHandles(procId))
            {
                StringBuilder message = new StringBuilder(1000);
                if (IsWindowVisible(handle))
                {
                    SendMessage(handle, WM_GETTEXT, message.Capacity, message);
                    if (message.Length > 0) return message.ToString();
                }

            }
            return "";
        }

以上代码的思路为:使用Win32_Process下的ManagementClass,获取到msmdsrv.exe对应的pid。

再通过一些系统API函数,获取到其对应的标题。

再利用CommandLine的特性,取到msmdsrv.exe对应的路径配合正则处理和路径拼接得到最终的msmdsrv.port.txt文件全路径。如此次笔者机器的路径为
C:\Users\Administrator\Microsoft\Power BI Desktop Store App\AnalysisServicesWorkspaces\AnalysisServicesWorkspace115089896\Data\msmdsrv.port.txt

C:\Users\Administrator\Microsoft\Power BI Desktop Store App\AnalysisServicesWorkspaces\AnalysisServicesWorkspace1855860078\Data\msmdsrv.port.txt

再利用此文件下存储着端口号信息的特性,最终将不同的msmdsrv.exe对应的端口号拿到手。

再利用AMO对象模型,将此端口号下的数据库名和Model名也读取到。

最终拿齐了所有信息后,可以回到Excel客户端去发起访问连接。
而在用户层面,使用窗体直观呈现关键可读性信息供用户选择不同的模型。

        private void formPbidNewConnect_Load(object sender, EventArgs e)
        {
            this.dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            List<Entity.PbidFileInfo> pbidPortTittleMappings = PbidConnection.GetPbidPortTittleMappings();
            foreach (var item in pbidPortTittleMappings)
            {
                this.dataGridView1.Rows.Add(new object[] { item.Port, item.FileName,item.DbName,item.ModelName });
            }
            this.dataGridView1.Rows[0].Selected = true;
        }

在数据透视表层面,只需构造出一条Oledb连接,用MSOLAP的provider即可。
具体代码如下:

        private void btnEnter_Click(object sender, EventArgs e)
        {
            DataGridViewRow row = this.dataGridView1.SelectedRows[0];
            Entity.PbidFileInfo pbidFileInfo = Common.GetPbidFileInfo(row);
            Excel.WorkbookConnection wkbcnn;
            if (Common.ExcelApp.ActiveWorkbook.Connections.Cast<Excel.WorkbookConnection>().Any(s=>s.Name==pbidFileInfo.FileName))
            {
                Common.ExcelApp.ActiveWorkbook.Connections[pbidFileInfo.FileName].Delete();
            }
            string wkbCnnString = $"OLEDB;Provider=MSOLAP;Integrated Security=SSPI;Persist Security Info=True;Data Source=localhost:{pbidFileInfo.Port};Initial Catalog={pbidFileInfo.DbName};";
            Common.ExcelApp.ActiveWorkbook.Connections.Add(pbidFileInfo.FileName, "pbidConnection", wkbCnnString, pbidFileInfo.ModelName, 1);
            Excel.Worksheet wht = Common.ExcelApp.Worksheets.Add();
            wkbcnn = Common.ExcelApp.ActiveWorkbook.Connections[pbidFileInfo.FileName];
            Excel.PivotCache pvtCache = Common.ExcelApp.ActiveWorkbook.PivotCaches().Create(Excel.XlPivotTableSourceType.xlExternal, wkbcnn);
            pvtCache.CreatePivotTable(wht.Range["A1"]);
            this.Close();
        }

结语

没有什么是不能分享的,为了社区的健康繁荣,Excel催化剂将最精华的最具商业价值的代码贡献给社区,也让中国的社区的声音能够更加的响亮,带出国际性的影响力。

技术交流QQ群

QQ群名:Excel催化剂开源讨论群, QQ群号:788145319

关于Excel催化剂

Excel催化剂先是一微信公众号的名称,后来顺其名称,正式推出了Excel插件,插件将持续性地更新,更新的周期视本人的时间而定争取一周能够上线一个大功能模块。Excel催化剂插件承诺个人用户永久性免费使用!

Excel催化剂插件使用最新的布署技术,实现一次安装,日后所有更新自动更新完成,无需重复关注更新动态,手动下载安装包重新安装,只需一次安装即可随时保持最新版本!

Excel催化剂插件下载链接:https://pan.baidu.com/s/1Iz2_NZJ8v7C9eqhNjdnP3Q

取名催化剂,因Excel本身的强大,并非所有人能够立马享受到,大部分人还是在被Excel软件所虐的阶段,就是头脑里很清晰想达到的效果,而且高手们也已经实现出来,就是自己怎么弄都弄不出来,或者更糟的是还不知道Excel能够做什么而停留在不断地重复、机械、手工地在做着数据,耗费着无数的青春年华岁月。所以催生了是否可以作为一种媒介,让广大的Excel用户们可以瞬间点燃Excel的爆点,无需苦苦地挣扎地没日没夜的技巧学习、高级复杂函数的烧脑,最终走向了从入门到放弃的道路。

最后Excel功能强大,其实还需树立一个观点,不是所有事情都要交给Excel去完成,也不是所有事情Excel都是十分胜任的,外面的世界仍然是一个广阔的世界,Excel只是其中一枚耀眼的明星,还有其他更多同样精彩强大的技术、工具等。*Excel催化剂也将借力这些其他技术,让Excel能够发挥更强大的爆发!

关于Excel催化剂作者

姓名:李伟坚,从事数据分析工作多年(BI方向),一名同样在路上的学习者。
服务过行业:零售特别是鞋服类的零售行业,电商(淘宝、天猫、京东、唯品会)

技术路线从一名普通用户,通过Excel软件的学习,从此走向数据世界,非科班IT专业人士。
历经重重难关,终于在数据的道路上达到技术平原期,学习众多的知识不再太吃力,同时也形成了自己的一套数据解决方案(数据采集、数据加工清洗、数据多维建模、数据报表展示等)。

擅长技术领域:Excel等Office家族软件、VBA&VSTO的二次开发、Sqlserver数据库技术、Sqlserver的商业智能BI技术、Powerbi技术、云服务器布署技术等等。

2018年开始职业生涯作了重大调整,从原来的正职工作,转为自由职业者,暂无固定收入,暂对前面道路不太明朗,苦重新回到正职工作,对Excel催化剂的运营和开发必定受到很大的影响(正职工作时间内不可能维护也不可能随便把工作时间内的成果公布于外,工作外的时间也十分有限,因已而立之年,家庭责任重大)。

和广大拥护者一同期盼:Excel催化剂一直能运行下去,我所惠及的群体们能够给予支持(多留言鼓励下、转发下朋友圈推荐、小额打赏下和最重点的可以和所在公司及同行推荐推荐,让我的技术可以在贵司发挥价值,实现双赢(初步设想可以数据顾问的方式或一些小型项目开发的方式合作)。

原文地址:https://www.cnblogs.com/ExcelCuiHuaJi/p/11225095.html

时间: 2024-11-05 18:44:56

Excel催化剂开源第47波-Excel与PowerBIDeskTop互通互联之第一篇的相关文章

Excel催化剂开源第51波-Excel催化剂遍历单元格操作性能保障

在Excel催化剂推出的这一年多时间里,经常性听到一种声音,大概意思是真正会写代码的人,都不会看上Excel催化剂写出来的功能,自己造一个更舒服贴心,仿佛会一点VBA就可以天下无敌一般,也好像Excel催化剂出的所有的功能,在他们眼中都是幼儿园级别,过家家玩玩而已. 这里不对此看法置评,只用事实说话,看看会一点VBA的人,能否造出以下的超强性能保障的代码功能来. 遍历单元格性能问题 学过VBA的群体,都知道遍历单元格是一个很慢的过程,最好将其放进数组中再遍历. 为何会慢这个问题,可能就没有深入研

Excel催化剂开源第50波-Excel与PowerBIDeskTop互通互联之第四篇

答应过的全盘分享,也必承诺到底,此篇PowerBI功能分享的最后一篇,讲述如何导出数据模型的元数据,笔者定义其为模型的数据字典. 此篇对应功能实现出自:第6波-导出PowerbiDesktop模型数据字典https://www.jianshu.com/p/bc26a8dcdfce 关系型数据库有数据字典,分析型数据库更加要有数据字典,DAX建模过程中,若模型的关系复杂,引用的表过多,生成的度量值.计算列过多,单单从PowerBIDeskTop去查看模型关系非常低效. 当然最好的查看方式,在全宇宙

Excel催化剂开源第48波-Excel与PowerBIDeskTop互通互联之第二篇

前一篇的分享中,主要谈到Excel透视表连接PowerBIDeskTop的技术,在访问SSAS模型时,不止可以使用透视表的方式访问,更可以发数据模型发起DAX或MDX查询,返回一个结果表数据,较透视表的方式更为灵活. 此篇再次带出Excel向PowerBIDeskTop发出DAX查询的技术要点. 此篇对应功能实现出自:第5波-使用DAX查询从PowerbiDeskTop中获取数据源https://www.jianshu.com/p/21b2ca8fd2b8 原理性分析 和上一篇类似,同样是使用O

Excel催化剂开源第40波-Excel插入图片做到极致的效果

不知道是开发人员的自我要求不高还是用户的使用宽容度足够大,在众多Excel插入图片的版本中,都没有考虑到许多的可大幅度提升用户体验的细节处理. Excel催化剂虽然开发水平有限,但也在有限的能力下,尽最大的努力做到自己可以过关的出街成品. 此篇着重谈下思路性的问题,代码因为太久远之前写的,现在看回也是一坨坨的,真需要时可能重新写一轮更方便. Excel插入图片的细节追求 可能在之前的文章中,也偶尔做了一些分享,自己也不太记得分享过哪些了,这里再重新梳理下,有重复提及的部分就权当复习好了. 一.插

Excel催化剂开源第43波-Excel选择对象Selection在.Net开发中的使用

Excel的二次开发有一极大的优势所在,可以结合用户的交互进行程序的运行,大量用户的交互,都是从选择对象开始,用户选择了单元格区域.图形.图表等对象,之后再进行程序代码的加工处理,生成用户所需的最终结果. 所以熟练处理选择对象,尤其关键,而在VBA里的使用方法,来到.Net中特别是C#语言下,就需要另外的代码处理,希望此篇的分享给VBA转VSTO的朋友们带来一些帮助指引. 在VBA中,判断一个Selection对象是什么类型的方法如下: Sub test() Dim sel Set sel =

个人永久性免费-Excel催化剂功能第20波-Excel与Sqlserver零门槛交互-数据上传篇

Excel作为众多数据存储的交换介质,在不同的系统内的数据很少可以很连贯地进行整合分析,一般的业务系统都会提供导出Excel作为标配功能供用户使用系统内生成的数据. 此时最大的问题是,Excel很维去成为各个数据源的整合方,其数据存储能力和运算能力一直是众多深度Excel用户们一大头痛的事情,当数据量一大,做什么操作都会慢下来. 今天Excel催化剂将给大家带来全新的解决方案,Excel与Sqlserver深度集成,在Excel上的数据源能轻松上传至Sqlserver中,实现以Sqlserver

Excel催化剂开源第1波-自定义函数的源代码全公开

Excel催化剂插件从2018年1月1日开始运营,到今天刚好一周年,在过去一年时间里,感谢社区里的许多友人们的关心和鼓励,得以坚持下来,并收获一定的用户量和粉丝数和少量的经济收入回报和个人知名度的提升. 虽然还没有达到预期的效果,但相信用心去做,无论是为理想.为情怀,为现实经济回报,在新的一年,仍然抱有感恩的心,继续贡献自我的一些微博之力. 在新的2019年,若时间精力的许可,可适当地开发Excel催化剂的源代码,供广大Excel爱好者特别是有开发能力的高级用户们一起学习观摩,算是再次给Exce

Excel催化剂开源第16波-VSTO开发之脱离传统COM交互以提升性能

在VSTO开发或其他COM技术开发过程中,甚至VBA也是,在和Excel交互中,难免会遇到性能瓶颈问题,COM技术的交互实在太慢,对大量数据读写等操作,耗时太长,容易卡用户界面以为是程序死机等等. 在专业程序猿开发群体,极少直接用COM的方式来访问Excel文件,都是以其他的非COM如最为流行的NPOI和EPPLUS类库的方式访问,并且是免费的可用于商业项目的. 在VSTO中调用NPOI和EPPLUS,又是怎样一翻境界呢?因Excel催化剂主要是为数据分析群体服务,其中大量的场景是面积大数据量的

个人永久性免费-Excel催化剂功能第47波-VBA开发者喜爱的加密函数类

VBA的确是个很不错的编程工具,寄生在OFFICE内,无需安装庞大的开发环境,即开即用,方便灵活,能实现的事情也很多,但毕竟VBA是微软停止更新维护将近20年的一种语言,计算机的世界发展速度有多快大家有目睹,今天抛砖引玉,拿几个加密类函数来讲说一下VSTO较VBA开发的一些优势所在. 文章出处说明 原文在简书上发表,再同步到Excel催化剂微信公众号或其他平台上,文章后续有修改和更新将在简书上操作, 其他平台不作同步修改更新,因此建议阅读其他出处的文章时,尽可能跳转回简书平台上查看. 题外话 本