アプリ版:「スタンプのみでお礼する」機能のリリースについて

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);
}

A 回答 (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

になっていた。
「excelVBAからC#へsendmes」の回答画像1
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/03/15 06:32

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!