excelVBAからC#へsendmessage

このQ&Aのポイント
  • excel VBAからC#のプログラムに文字列を渡す方法について考えています。
  • 作成したプログラムがうまく動作せず、表示される文字列がおかしいです。
  • Windows 7、Excel 2010、.NET Framework 4を使用しています。
回答を見る
  • ベストアンサー

excelVBAからC#へsendmessage

excelのVBAから文字列をsendmessageで C#のプログラムに文字列を渡せないかと考えています。 ネットで調べつつなんとか作ってみたのですが、 どうしてもうまく動作しません。 変な文字列が表示されてしまいます。 どこがおかしいか教えて頂けないでしょうか。 windows7、Excel2010、.netFramework4になります。 ※※※excel VBA側プログラム※※※※※※※※※※※※ //外部functionを使いますよ Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" _ (ByVal hwd As Long, ByVal Msg As Long, ByVal wpara As Long, lpara As COPYDATASTRUCT) As Long //構造体 Public Type COPYDATASTRUCT dwData As Long cbData As Long lpData As String End Type //メッセージを送信するsub Public Sub sousin() Dim result As Longv Dim hWnd As Long Dim cds As COPYDATASTRUCT Dim str As String Dim strby() As Byte Dim length As Long  ~ ウィンドウハンドルの取得 ~ str = "test" strby = StrConv(str, vbFromUnicode) length = UBound(strby) - LBound(strby) + 1 cds.dwData = 0 cds.lpData = str cds.cbData = length result = SendMessage(hWnd, WM_COPYDATA, 0, cds) End Sub ※※※C#側プログラム※※※※※※※※※※※※※※※ //構造体 public struct COPYDATASTRUCT { public long dwData; public long cbData; public string lpData; } //WndProc関数 protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_COPYDATA: COPYDATASTRUCT mystr = new COPYDATASTRUCT(); Type mytype = mystr.GetType(); mystr = (COPYDATASTRUCT)m.GetLParam(mytype); label1.Text = mystr.lpData;           ←※無茶苦茶な文字列になります break; } base.WndProc(ref m); }

質問者が選んだベストアンサー

  • ベストアンサー
回答No.1

Win32 APIをあまり使ったことないんで自信ない。 なお、俺の環境はVista(32 bit)である。 C言語の知識があると尚可 http://codezine.jp/article/detail/1718 を参考に、SendMessageAではなくSendMessageWを呼んでみた。 一応手元では成功している。(ハンドルを調べるためにC#のプログラムを先に起動している) '===========VBA側プログラム============= Option Explicit ' このVBのプログラムからは以下の引数のSendMessageという関数を呼び出した時、 ' user32.dllにあるSendMessageWを呼び出すものとする、というものだと思っているので ' ぶっちゃけわかればAliasの後ろはなんだっていいんだと思う。 Declare Function SendMessage Lib "user32.dll" Alias "SendMessageW" _ (ByVal hwd As Long, ByVal Msg As Long, ByVal wpara As Long, lpara As COPYDATASTRUCT) As Long '構造体の定義 '64bit環境ではどうなるんだろうね(Windows 7の人とかだと多そう) Public Type COPYDATASTRUCT dwData As Long ' 32 bit cbData As Long ' 32 bit lpData As Long ' 変更: 文字列へのポインタを格納することに End Type '元のコードから抜けてた。 Public Const WM_COPYDATA As Integer = &H4A Public Sub Send() Dim result As Long Dim hWnd As Long Dim cds As COPYDATASTRUCT Dim str As String 'ハンドルを調べる方法を調べるのが面倒だったので '先にC#のプログラム起動して調べた。 '起動毎に変わるので注意。> 俺 hWnd = 657792 str = "あいう" cds.dwData = 0 cds.lpData = StrPtr(str) 'サロゲートペア文字とか考慮しなければUTF-16は1文字2バイト固定 'Wの場合null文字の分要らないっぽい?。 cds.cbData = 2 * Len(str) result = SendMessage(hWnd, WM_COPYDATA, 0, cds) End Sub //============================C#側プログラム================ using System; using System.Windows.Forms; using System.Runtime.InteropServices; class Hoge:System.Windows.Forms.Form{ public const int WM_COPYDATA = 0x4A; // 自信ないけど、多分メモリの構造とか合わせないといけないと思うので // この属性をつけておく [StructLayout(LayoutKind.Sequential)] public struct COPYDATASTRUCT { // VBのLongは32bitだけど、.NET FrameworkのLongは64bit // だと思う。一応uintを使っておく。 public uint dwData; public uint cbData; // SendMessageWでUnicode文字列を送ってくるのでこの属性を一応つけてみる。 //あってるか自信なし。 [MarshalAs(UnmanagedType.LPWStr)] public string lpData; } //WndProc関数 protected override void WndProc(ref Message m){ // Textプロパティを書き換えて何度もここが走ったりしないか不安だったので // デバッグのため、一応ファイルに書き出すことに。 // 前のものを書き換えないよう、追記で。 // 自信があるなら別になくて良い処理 using(System.IO.StreamWriter sw = new System.IO.StreamWriter("D:\\logging.txt",true)){ sw.WriteLine ("test:" + m.Msg); sw.WriteLine ("wanted:" + WM_COPYDATA); switch (m.Msg){ case WM_COPYDATA: COPYDATASTRUCT mystr = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT)); sw.WriteLine (mystr.lpData + mystr.lpData.Length); break; } } base.WndProc(ref m); } } class Fuga{ public static void Main(){ Hoge f = new Hoge(); // 先に起動して調べておく f.Text = f.Handle.ToString (); f.ShowDialog (); } } ======================================= とまぁこんな感じにしておいて、 WM_COPYDATAに該当する74(=0x4A)を探したら test:74 あいう3 になっていた。

tokati_nananana
質問者

お礼

ありがとうございました! おかげさまで上手いこと動きました! C#側のlongをuintに変えて、 VBA側の文字列の長さをLen(str)に変えたらうまく動きました。 また、その他にもいろいろと私のしらないテクニックを見ることができて、 非常に興味深かったです! 本当にありがとうございました!

関連するQ&A

  • SendMessageについて

    VERSION:VB6.0 SendMessageを使用しSQLPlusに対して文字列を送りたいのですが巧くいきません。 ↓が自身が作成したSendMessageを使用しているプログラムの一部なのですが、おかしな点や別な方法があればご教授お願いします。 '別アプリにメッセージを送る Declare Function SendMessageStr Lib "user32.dll" _ Alias "SendMessageA" (ByVal hWnd As Long, ByVal MSG As Long, _ ByVal wParam As Long, ByVal lParam As String) As Long Dim pId As Long 'プロセスID pId = Shell("SQLPlusのアドレス" & " /nolog", 1) SendMessageStr pId, WM_SETTEXT, 0, "送信する文字列" というプログラムです。 SQLPlusは起動するのですが文字列がSQLPlus側に送れず困っています。 どうかよろしくお願いします。

  • ExcelVBAで他のアプリをスクロールさせたい

    エクセルVBAから 他のアプリのスクロールバーを操作して、指定範囲で画面スクロールしたいと思っています。 キー入力では操作出来ない(マウス操作でのみスクロールされる)アプリなので、 Sendkeysは使えないのではないかと思い、 APIでハンドルを取得して、 SendMessageすればできるかなと思いましたが、APIについてよく分からないので、 とりあえず、メモ帳で以下を作成してみました。しかし、スクロールされません。 どこがいけないのか教えていただけないでしょうか? よろしくお願いします。 *************** Public Declare Function FindWindowA Lib "User32" (ByVal cnm As String, ByVal cap As String) As Long Public Declare Function SendMessage Lib "User32" Alias "SendMessageA" _ (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Public Const WM_VSCROLL = &H115 Public Const WM_HSCROLL = &H114 Public Const SB_TOP = &H6& Public Const SB_BOTTOM = &H7& Sub handle_get()  Dim Handle As Long  Dim Ap1 As String  Ap1 = "a.txt - メモ帳"  AppActivate Ap1  Handle = FindWindowA(vbNullString, Ap1)  SendMessage Handle, WM_VSCROLL, SB_BOTTOM, ByVal CLng(0)  SendMessage Handle, WM_HSCROLL, SB_TOP, ByVal CLng(0) End Sub ***************

  • VB.NETのSendMessageを教えてください

    SendMessageというAPIを試しているのですが、まず試しに Button2.Text = "test" と同じ結果をSendMessageでやってみたいのですが 下のようにしてみたのですが、変更になりませんでした。 どのようにすれば良いかご教授頂ければ幸いです。よろしくお願致します。 Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer Private Const WM_SETTEXT As Integer = &HC Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim h As Integer h = Me.Button2.Handle.ToInt32 SendMessage(h, WM_SETTEXT, 0&, "test") End Sub VB.NET2003 FrameWork1.1 WindowsXP-PRO(SP2) です。

  • CreateFileで開いたファイル名を取得するには?

    CreateFile関数で開いたファイル名を取得して、エディットボックスに表示させるプログラムを作りたいと思っています。 sendmessageを使って表示させることを考えてみたのですが…。 以下にソースを載せます。 // メインWindowへ通知するデータを作成 COPYDATASTRUCT cds; cds.lpData = pszFileName; //CreateFileの第一引数pszFileName(ファイル名)を格納 cds.cbData = lstrlen(pszFileName) + 1; cds.dwData = 0; // メインWindowを検索 HWND hWnd = FindWindow(NULL, _T("メインウインドウの名前")); if(hWnd) SendMessage(hWnd, WM_COPYDATA, NULL, (LPARAM)&cds); という風では、取得できないのでしょうか? よろしくお願いします。

  • Cで作成したDLL関数をVBから呼ぶ

    以前にあった「Cで作成したDLL関数をVBから呼び 引数渡し方法」を試したのですがVB側でデータが受け取れません。 http://oshiete1.goo.ne.jp/kotaeru.php3?q=711327 C側での値設定がわるいのでしょうか? VB側 Public Declare Function testAP Lib "C:\bin\test.dll" (ByVal lpKeyData As String) As Long --------------------------------------------------------------------------- Dim lngRc As Long Dim keydata As String * 128 keydata = String$(128, Chr(0) & Chr(0)) lngRc = testAP(keydata) VC側 __declspec(dllexport) long __stdcall testAP(char *lpdata){ →C側でlpdataに値を設定する lpdata="ABCDEF"; }

  • SendMessageによるチェックボックスの状態取得

    はじめまして、VB.NET2005でチェックボックスの状態の取得、設定をうまく設定できません。OSはxpです。 Public Class Form1 Private Declare Function FindWindow Lib "user32" Alias "FindWindowA"  (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Integer Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Integer, _ ByVal hWnd2 As Integer, ByVal lpsz1 As String, ByVal lpsz2 As String) As Integer Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, _ ByVal wMsg As Integer, ByVal wParam As Integer, ByVal iParam As String) As Integer Private Declare Function SendMessageint Lib "user32" Alias "SendMessageA" (ByVal hwnd As Integer, _ ByVal wMsg As Integer, ByVal wParam As Integer, ByVal iParam As Integer) As Integer Const BM_GETCHECK = &HF0 Const BM_GETSTATE = &HF2 Const BM_SETCHECK = &HF1 Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Dim hWindows As Integer Dim ipEDIT As Integer Dim i As Integer hWindows = FindWindow(vbNullString, "Form1") '198458 ipEDIT = FindWindowEx(hWindows, 0, vbNullString, "CheckBox1") MessageBox.Show(ipEDIT) i = SendMessageint(ipEDIT, BM_GETCHECK, 0, 0) 'SendMessageint(ipEDIT, BM_SETCHECK, 1, 0) MessageBox.Show(i) End Sub End Class のようなコードなのですが、 ハンドルは取得できているのですが、 SendMessageの戻り値は0になります。 勿論、コメントのチェックをセットも出来ません。 ご教授のほど宜しくお願いします。

  • vbaで配列に値を格納する場合

    vbaで配列に値を格納する場合 変数の宣言はどちらを使った方が良いのでしょうか? Sub Sample1() Dim i As Long Dim myStr As String Dim tmp() As String myStr = "a,i,u,e,o" tmp = Split(myStr, ",") End Sub か Sub Sample1() Dim i As Long Dim myStr As String Dim tmp As Variant myStr = "a,i,u,e,o" tmp = Split(myStr, ",") End Sub でも問題なく動くのですが、 Variant型での宣言はあまりしない方が良いですか? あと Dim tmp() As String ならエラーにならないのですが Dim tmp As String だとエラーになってしまう理由がよくわからないので教えて頂けますか?

  • WinAPIで電卓をクリック

    現在、WinAPIを勉強しており、練習としてVBAを用いて、電卓アプリのボタンをクリックしようとしています。 キーを送るのではなく、クリックで行いたいたいと 考えています。 ボタンのハンドルを取得するところまではできましたが、sendMessageでクリックできず、EditBoxに数字が 入りません。 どのようにすればよいのかご教授ください。 よろしくお願い致します。 環境: WinXP home、 Excel2002、Win付属アプリの電卓v5.1 ---作成したプログラム---- '標準モジュールの中身 Option Explicit Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long Private Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hWnd As Long) As Long Private Const WM_LBUTTONDOWN = &H201 Private Const WM_LBUTTONUP = &H202 Sub Main() Dim lngWindWnd As Long 'ウィンドウハンドル Dim ret As Long Dim hCalc As Long 'アプリケーションタイトルより、ウィンドウハンドルを得ます lngWindWnd = FindWindow(vbNullString, "電卓") '8ボタンのハンドル(確実に取れていることを確認 hCalc = FindWindowEx(lngWindWnd, 0, "Button", "8") ret = SetForegroundWindow(lngWindWnd) ret = SendMessage(hCalc, WM_LBUTTONDOWN, 0, 0) End Sub

  • VBA(Excel2003)で文字列の切り出し

    下のプロシージャーで全角半角混じりの文字列を切り出し、別の文字列で結合しようと思いますがうまくいく場合といかない場合があります。 イミディエイト・ウィンドウ上とCell上で動作が違います。 Cell上でうまく表示させるにはどうしたらいいでしょうか? Sub Test() Dim myString(2) As String Dim i As Integer myString(0) = "airueo" myString(1) = "かきくけこ" myString(2) = "さシすせそ" For i = 0 To 2 Debug.Print MidMbcs(myString(i), 1, 5) & "...テスト" Cells(i + 1, 1).Value = MidMbcs(myString(i), 1, 5) & "...テスト" Next i End Sub Function LenMbcs(ByVal str As String) LenMbcs = LenB(StrConv(str, vbFromUnicode)) End Function Function MidMbcs(ByVal str As String, start, length) MidMbcs = StrConv(MidB(StrConv(str, vbFromUnicode), start, length), vbUnicode) End Function

  • ExcelVBAでのkernel32(64bit)

    今までExcel2000のVBAから、以下のようなコードを使ってC++で作ったコマンドプロンプトで動くプログラムを動かすプログラムを作っていましたが、これを64bitのWindows7上で動いているExcel2010で使おうとしたらメッセージが出ました。いろいろ調べてみたところ、たぶんDeclareにPtrSafeを付ければ良いようなのですが、その際、他のコードはそのままで良いのでしょうか。特に、コード中のLongはそのままで良いのか気になるのですが...。ちなみに、下記コードの条件コンパイルはネットで調べて見よう見まねで付けたもので、Excel2000のときには付けていないものでした。ご存じの方がいらっしゃいましたらご教授ください。 '------------------------------------------------------------------------------ ' Win32 API関数・定数の宣言 '------------------------------------------------------------------------------ #If VBA7 And Win64 Then '64bit Declare PtrSafe Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, _   ByVal dwMilliseconds As Long) As Long Declare PtrSafe Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _   ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long #Else '32bit Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, _   ByVal dwMilliseconds As Long) As Long Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _   ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long #End If Private Const PROCESS_ALL_ACCESS As Long = &H1F0FFF Private Const INFINITE As Long = &HFFFF '------------------------------------------------------------------------------ ' Run '------------------------------------------------------------------------------ Public Sub Run(ByVal project_name As String)   Dim program As String   Dim task_id As Long   Dim h_proc As Variant   program = mdlFunc.ProgramPath() & mdlFunc.ProgramOption(project_name) 'プログラム名   task_id = Shell(program, vbHide)   h_proc = OpenProcess(PROCESS_ALL_ACCESS, False, task_id)   If OpenProcess(PROCESS_ALL_ACCESS, False, task_id) <> vbNull Then     Call WaitForSingleObject(h_proc, INFINITE)     CloseHandle h_proc   End If End Sub

専門家に質問してみよう